Failed Conditions
Pull Request — master (#1145)
by
unknown
01:56
created

src/Facebook.php (2 issues)

1
<?php
2
/**
3
 * Copyright 2017 Facebook, Inc.
4
 *
5
 * You are hereby granted a non-exclusive, worldwide, royalty-free license to
6
 * use, copy, modify, and distribute this software in source code or binary
7
 * form for use in connection with the web services and APIs provided by
8
 * Facebook.
9
 *
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
 *
15
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21
 * DEALINGS IN THE SOFTWARE.
22
 */
23
namespace Facebook;
24
25
use Facebook\Authentication\AccessToken;
26
use Facebook\Authentication\OAuth2Client;
27
use Facebook\FileUpload\File;
28
use Facebook\FileUpload\ResumableUploader;
29
use Facebook\FileUpload\TransferChunk;
30
use Facebook\FileUpload\Video;
31
use Facebook\GraphNode\GraphEdge;
32
use Facebook\Url\UrlDetectionInterface;
33
use Facebook\Url\UrlDetectionHandler;
34
use Facebook\PersistentData\PersistentDataFactory;
35
use Facebook\PersistentData\PersistentDataInterface;
36
use Facebook\Helper\CanvasHelper;
37
use Facebook\Helper\JavaScriptHelper;
38
use Facebook\Helper\PageTabHelper;
39
use Facebook\Helper\RedirectLoginHelper;
40
use Facebook\Exception\SDKException;
41
use Psr\Http\Client\ClientInterface;
42
use Psr\Http\Message\RequestFactoryInterface;
43
use Psr\Http\Message\StreamFactoryInterface;
44
45
/**
46
 * @package Facebook
47
 */
48
class Facebook
49
{
50
    /**
51
     * @const string Version number of the Facebook PHP SDK.
52
     */
53
    const VERSION = '6.0-dev';
54
55
    /**
56
     * @const string The name of the environment variable that contains the app ID.
57
     */
58
    const APP_ID_ENV_NAME = 'FACEBOOK_APP_ID';
59
60
    /**
61
     * @const string The name of the environment variable that contains the app secret.
62
     */
63
    const APP_SECRET_ENV_NAME = 'FACEBOOK_APP_SECRET';
64
65
    /**
66
     * @var Application the Application entity
67
     */
68
    protected $app;
69
70
    /**
71
     * @var Client the Facebook client service
72
     */
73
    protected $client;
74
75
    /**
76
     * @var OAuth2Client The OAuth 2.0 client service.
77
     */
78
    protected $oAuth2Client;
79
80
    /**
81
     * @var null|UrlDetectionInterface the URL detection handler
82
     */
83
    protected $urlDetectionHandler;
84
85
    /**
86
     * @var null|AccessToken the default access token to use with requests
87
     */
88
    protected $defaultAccessToken;
89
90
    /**
91
     * @var null|string the default Graph version we want to use
92
     */
93
    protected $defaultGraphVersion;
94
95
    /**
96
     * @var null|PersistentDataInterface the persistent data handler
97
     */
98
    protected $persistentDataHandler;
99
100
    /**
101
     * @var null|BatchResponse|Response stores the last request made to Graph
102
     */
103
    protected $lastResponse;
104
105
    /**
106
     * Instantiates a new Facebook super-class object.
107
     *
108
     * @param ClientInterface $client
109
     * @param RequestFactoryInterface $requestFactory
110
     * @param StreamFactoryInterface $streamFactory
111
     * @param array $config
112
     *
113
     * @throws SDKException
114
     */
115 15
    public function __construct(
116
        ClientInterface $client,
117
        RequestFactoryInterface $requestFactory,
118
        StreamFactoryInterface $streamFactory,
119
        array $config = [])
0 ignored issues
show
The closing parenthesis of a multi-line function declaration must be on a new line
Loading history...
120
    {
0 ignored issues
show
The closing parenthesis and the opening brace of a multi-line function declaration must be on the same line
Loading history...
121 15
        $config = array_merge([
122 15
            'app_id' => getenv(static::APP_ID_ENV_NAME),
123 15
            'app_secret' => getenv(static::APP_SECRET_ENV_NAME),
124
            'default_graph_version' => null,
125
            'enable_beta_mode' => false,
126
            'persistent_data_handler' => null,
127
            'url_detection_handler' => null,
128 15
        ], $config);
129
130 15
        if (!$config['app_id']) {
131 1
            throw new SDKException('Required "app_id" key not supplied in config and could not find fallback environment variable "' . static::APP_ID_ENV_NAME . '"');
132
        }
133 14
        if (!$config['app_secret']) {
134 1
            throw new SDKException('Required "app_secret" key not supplied in config and could not find fallback environment variable "' . static::APP_SECRET_ENV_NAME . '"');
135
        }
136 13
        if (!$config['default_graph_version']) {
137 1
            throw new \InvalidArgumentException('Required "default_graph_version" key not supplied in config');
138
        }
139
140 12
        $this->app = new Application($config['app_id'], $config['app_secret']);
141 12
        $this->client = new Client(
142 12
            $client,
143 12
            $requestFactory,
144 12
            $streamFactory,
145 12
            $config['enable_beta_mode']
146
        );
147 12
        $this->setUrlDetectionHandler($config['url_detection_handler'] ?: new UrlDetectionHandler());
148 11
        $this->persistentDataHandler = PersistentDataFactory::createPersistentDataHandler(
149 11
            $config['persistent_data_handler']
150
        );
151
152 10
        if (isset($config['default_access_token'])) {
153 3
            $this->setDefaultAccessToken($config['default_access_token']);
154
        }
155
156 9
        $this->defaultGraphVersion = $config['default_graph_version'];
157 9
    }
158
159
    /**
160
     * Returns the Application entity.
161
     *
162
     * @return Application
163
     */
164 2
    public function getApplication()
165
    {
166 2
        return $this->app;
167
    }
168
169
    /**
170
     * Returns the Client service.
171
     *
172
     * @return Client
173
     */
174 4
    public function getClient()
175
    {
176 4
        return $this->client;
177
    }
178
179
    /**
180
     * Returns the OAuth 2.0 client service.
181
     *
182
     * @return OAuth2Client
183
     */
184 2
    public function getOAuth2Client()
185
    {
186 2
        if (!$this->oAuth2Client instanceof OAuth2Client) {
187 2
            $app = $this->getApplication();
188 2
            $client = $this->getClient();
189 2
            $this->oAuth2Client = new OAuth2Client($app, $client, $this->defaultGraphVersion);
190
        }
191
192 2
        return $this->oAuth2Client;
193
    }
194
195
    /**
196
     * Returns the last response returned from Graph.
197
     *
198
     * @return null|BatchResponse|Response
199
     */
200
    public function getLastResponse()
201
    {
202
        return $this->lastResponse;
203
    }
204
205
    /**
206
     * Returns the URL detection handler.
207
     *
208
     * @return UrlDetectionInterface
209
     */
210 1
    public function getUrlDetectionHandler()
211
    {
212 1
        return $this->urlDetectionHandler;
213
    }
214
215
    /**
216
     * Changes the URL detection handler.
217
     *
218
     * @param UrlDetectionInterface $urlDetectionHandler
219
     */
220 11
    private function setUrlDetectionHandler(UrlDetectionInterface $urlDetectionHandler)
221
    {
222 11
        $this->urlDetectionHandler = $urlDetectionHandler;
223 11
    }
224
225
    /**
226
     * Returns the default AccessToken entity.
227
     *
228
     * @return null|AccessToken
229
     */
230 2
    public function getDefaultAccessToken()
231
    {
232 2
        return $this->defaultAccessToken;
233
    }
234
235
    /**
236
     * Sets the default access token to use with requests.
237
     *
238
     * @param AccessToken|string $accessToken the access token to save
239
     *
240
     * @throws \InvalidArgumentException
241
     */
242 5
    public function setDefaultAccessToken($accessToken)
243
    {
244 5
        if (is_string($accessToken)) {
245 3
            $this->defaultAccessToken = new AccessToken($accessToken);
246
247 3
            return;
248
        }
249
250 2
        if ($accessToken instanceof AccessToken) {
251 1
            $this->defaultAccessToken = $accessToken;
252
253 1
            return;
254
        }
255
256 1
        throw new \InvalidArgumentException('The default access token must be of type "string" or Facebook\AccessToken');
257
    }
258
259
    /**
260
     * Returns the default Graph version.
261
     *
262
     * @return string
263
     */
264
    public function getDefaultGraphVersion()
265
    {
266
        return $this->defaultGraphVersion;
267
    }
268
269
    /**
270
     * Returns the redirect login helper.
271
     *
272
     * @return RedirectLoginHelper
273
     */
274 2
    public function getRedirectLoginHelper()
275
    {
276 2
        return new RedirectLoginHelper(
277 2
            $this->getOAuth2Client(),
278 2
            $this->persistentDataHandler,
279 2
            $this->urlDetectionHandler
280
        );
281
    }
282
283
    /**
284
     * Returns the JavaScript helper.
285
     *
286
     * @return JavaScriptHelper
287
     */
288
    public function getJavaScriptHelper()
289
    {
290
        return new JavaScriptHelper($this->app, $this->client, $this->defaultGraphVersion);
291
    }
292
293
    /**
294
     * Returns the canvas helper.
295
     *
296
     * @return CanvasHelper
297
     */
298
    public function getCanvasHelper()
299
    {
300
        return new CanvasHelper($this->app, $this->client, $this->defaultGraphVersion);
301
    }
302
303
    /**
304
     * Returns the page tab helper.
305
     *
306
     * @return PageTabHelper
307
     */
308
    public function getPageTabHelper()
309
    {
310
        return new PageTabHelper($this->app, $this->client, $this->defaultGraphVersion);
311
    }
312
313
    /**
314
     * Sends a GET request to Graph and returns the result.
315
     *
316
     * @param string                  $endpoint
317
     * @param null|AccessToken|string $accessToken
318
     * @param null|string             $eTag
319
     * @param null|string             $graphVersion
320
     *
321
     * @throws SDKException
322
     *
323
     * @return Response
324
     */
325
    public function get($endpoint, $accessToken = null, $eTag = null, $graphVersion = null)
326
    {
327
        return $this->sendRequest(
328
            'GET',
329
            $endpoint,
330
            $params = [],
331
            $accessToken,
332
            $eTag,
333
            $graphVersion
334
        );
335
    }
336
337
    /**
338
     * Sends a POST request to Graph and returns the result.
339
     *
340
     * @param string                  $endpoint
341
     * @param array                   $params
342
     * @param null|AccessToken|string $accessToken
343
     * @param null|string             $eTag
344
     * @param null|string             $graphVersion
345
     *
346
     * @throws SDKException
347
     *
348
     * @return Response
349
     */
350
    public function post($endpoint, array $params = [], $accessToken = null, $eTag = null, $graphVersion = null)
351
    {
352
        return $this->sendRequest(
353
            'POST',
354
            $endpoint,
355
            $params,
356
            $accessToken,
357
            $eTag,
358
            $graphVersion
359
        );
360
    }
361
362
    /**
363
     * Sends a DELETE request to Graph and returns the result.
364
     *
365
     * @param string                  $endpoint
366
     * @param array                   $params
367
     * @param null|AccessToken|string $accessToken
368
     * @param null|string             $eTag
369
     * @param null|string             $graphVersion
370
     *
371
     * @throws SDKException
372
     *
373
     * @return Response
374
     */
375
    public function delete($endpoint, array $params = [], $accessToken = null, $eTag = null, $graphVersion = null)
376
    {
377
        return $this->sendRequest(
378
            'DELETE',
379
            $endpoint,
380
            $params,
381
            $accessToken,
382
            $eTag,
383
            $graphVersion
384
        );
385
    }
386
387
    /**
388
     * Sends a request to Graph for the next page of results.
389
     *
390
     * @param GraphEdge $graphEdge the GraphEdge to paginate over
391
     *
392
     * @throws SDKException
393
     *
394
     * @return null|GraphEdge
395
     */
396
    public function next(GraphEdge $graphEdge)
397
    {
398
        return $this->getPaginationResults($graphEdge, 'next');
399
    }
400
401
    /**
402
     * Sends a request to Graph for the previous page of results.
403
     *
404
     * @param GraphEdge $graphEdge the GraphEdge to paginate over
405
     *
406
     * @throws SDKException
407
     *
408
     * @return null|GraphEdge
409
     */
410
    public function previous(GraphEdge $graphEdge)
411
    {
412
        return $this->getPaginationResults($graphEdge, 'previous');
413
    }
414
415
    /**
416
     * Sends a request to Graph for the next page of results.
417
     *
418
     * @param GraphEdge $graphEdge the GraphEdge to paginate over
419
     * @param string    $direction the direction of the pagination: next|previous
420
     *
421
     * @throws SDKException
422
     *
423
     * @return null|GraphEdge
424
     */
425
    public function getPaginationResults(GraphEdge $graphEdge, $direction)
426
    {
427
        $paginationRequest = $graphEdge->getPaginationRequest($direction);
428
        if (!$paginationRequest) {
429
            return null;
430
        }
431
432
        $this->lastResponse = $this->client->sendRequest($paginationRequest);
433
434
        // Keep the same GraphNode subclass
435
        $subClassName = $graphEdge->getSubClassName();
436
        $graphEdge = $this->lastResponse->getGraphEdge($subClassName, false);
437
438
        return count($graphEdge) > 0 ? $graphEdge : null;
439
    }
440
441
    /**
442
     * Sends a request to Graph and returns the result.
443
     *
444
     * @param string                  $method
445
     * @param string                  $endpoint
446
     * @param array                   $params
447
     * @param null|AccessToken|string $accessToken
448
     * @param null|string             $eTag
449
     * @param null|string             $graphVersion
450
     *
451
     * @throws SDKException
452
     *
453
     * @return Response
454
     */
455
    public function sendRequest($method, $endpoint, array $params = [], $accessToken = null, $eTag = null, $graphVersion = null)
456
    {
457
        $accessToken = $accessToken ?: $this->defaultAccessToken;
458
        $graphVersion = $graphVersion ?: $this->defaultGraphVersion;
459
        $request = $this->request($method, $endpoint, $params, $accessToken, $eTag, $graphVersion);
460
461
        return $this->lastResponse = $this->client->sendRequest($request);
462
    }
463
464
    /**
465
     * Sends a batched request to Graph and returns the result.
466
     *
467
     * @param array                   $requests
468
     * @param null|AccessToken|string $accessToken
469
     * @param null|string             $graphVersion
470
     *
471
     * @throws SDKException
472
     *
473
     * @return BatchResponse
474
     */
475
    public function sendBatchRequest(array $requests, $accessToken = null, $graphVersion = null)
476
    {
477
        $accessToken = $accessToken ?: $this->defaultAccessToken;
478
        $graphVersion = $graphVersion ?: $this->defaultGraphVersion;
479
        $batchRequest = new BatchRequest(
480
            $this->app,
481
            $requests,
482
            $accessToken,
483
            $graphVersion
484
        );
485
486
        return $this->lastResponse = $this->client->sendBatchRequest($batchRequest);
487
    }
488
489
    /**
490
     * Instantiates an empty BatchRequest entity.
491
     *
492
     * @param null|AccessToken|string $accessToken  The top-level access token. Requests with no access token
493
     *                                              will fallback to this.
494
     * @param null|string             $graphVersion the Graph API version to use
495
     *
496
     * @return BatchRequest
497
     */
498 1
    public function newBatchRequest($accessToken = null, $graphVersion = null)
499
    {
500 1
        $accessToken = $accessToken ?: $this->defaultAccessToken;
501 1
        $graphVersion = $graphVersion ?: $this->defaultGraphVersion;
502
503 1
        return new BatchRequest(
504 1
            $this->app,
505 1
            [],
506 1
            $accessToken,
507 1
            $graphVersion
508
        );
509
    }
510
511
    /**
512
     * Instantiates a new Request entity.
513
     *
514
     * @param string                  $method
515
     * @param string                  $endpoint
516
     * @param array                   $params
517
     * @param null|AccessToken|string $accessToken
518
     * @param null|string             $eTag
519
     * @param null|string             $graphVersion
520
     *
521
     * @throws SDKException
522
     *
523
     * @return Request
524
     */
525 1
    public function request($method, $endpoint, array $params = [], $accessToken = null, $eTag = null, $graphVersion = null)
526
    {
527 1
        $accessToken = $accessToken ?: $this->defaultAccessToken;
528 1
        $graphVersion = $graphVersion ?: $this->defaultGraphVersion;
529
530 1
        return new Request(
531 1
            $this->app,
532 1
            $accessToken,
533 1
            $method,
534 1
            $endpoint,
535 1
            $params,
536 1
            $eTag,
537 1
            $graphVersion
538
        );
539
    }
540
541
    /**
542
     * Factory to create File's.
543
     *
544
     * @param string $pathToFile
545
     *
546
     * @throws SDKException
547
     *
548
     * @return File
549
     */
550
    public function fileToUpload($pathToFile)
551
    {
552
        return new File($pathToFile);
553
    }
554
555
    /**
556
     * Factory to create Video's.
557
     *
558
     * @param string $pathToFile
559
     *
560
     * @throws SDKException
561
     *
562
     * @return Video
563
     */
564 2
    public function videoToUpload($pathToFile)
565
    {
566 2
        return new Video($pathToFile);
567
    }
568
569
    /**
570
     * Upload a video in chunks.
571
     *
572
     * @param int         $target           the id of the target node before the /videos edge
573
     * @param string      $pathToFile       the full path to the file
574
     * @param array       $metadata         the metadata associated with the video file
575
     * @param null|string $accessToken      the access token
576
     * @param int         $maxTransferTries the max times to retry a failed upload chunk
577
     * @param null|string $graphVersion     the Graph API version to use
578
     *
579
     * @throws SDKException
580
     *
581
     * @return array
582
     */
583 2
    public function uploadVideo($target, $pathToFile, $metadata = [], $accessToken = null, $maxTransferTries = 5, $graphVersion = null)
584
    {
585 2
        $accessToken = $accessToken ?: $this->defaultAccessToken;
586 2
        $graphVersion = $graphVersion ?: $this->defaultGraphVersion;
587
588 2
        $uploader = new ResumableUploader($this->app, $this->client, $accessToken, $graphVersion);
589 2
        $endpoint = '/'.$target.'/videos';
590 2
        $file = $this->videoToUpload($pathToFile);
591 2
        $chunk = $uploader->start($endpoint, $file);
592
593
        do {
594 2
            $chunk = $this->maxTriesTransfer($uploader, $endpoint, $chunk, $maxTransferTries);
595 1
        } while (!$chunk->isLastChunk());
596
597
        return [
598 1
          'video_id' => $chunk->getVideoId(),
599 1
          'success' => $uploader->finish($endpoint, $chunk->getUploadSessionId(), $metadata),
600
        ];
601
    }
602
603
    /**
604
     * Attempts to upload a chunk of a file in $retryCountdown tries.
605
     *
606
     * @param ResumableUploader $uploader
607
     * @param string            $endpoint
608
     * @param TransferChunk     $chunk
609
     * @param int               $retryCountdown
610
     *
611
     * @throws SDKException
612
     *
613
     * @return TransferChunk
614
     */
615 2
    private function maxTriesTransfer(ResumableUploader $uploader, $endpoint, TransferChunk $chunk, $retryCountdown)
616
    {
617 2
        $newChunk = $uploader->transfer($endpoint, $chunk, $retryCountdown < 1);
618
619 2
        if ($newChunk !== $chunk) {
620 1
            return $newChunk;
621
        }
622
623 1
        $retryCountdown--;
624
625
        // If transfer() returned the same chunk entity, the transfer failed but is resumable.
626 1
        return $this->maxTriesTransfer($uploader, $endpoint, $chunk, $retryCountdown);
627
    }
628
}
629