Completed
Push — master ( 470970...a080c6 )
by
unknown
05:31
created

Cursor::key()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1
Metric Value
dl 0
loc 3
ccs 2
cts 2
cp 1
rs 10
cc 1
eloc 2
nc 1
nop 0
crap 1
1
<?php
2
/**
3
 * Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
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
 */
24
25
namespace FacebookAds;
26
27
use FacebookAds\Http\RequestInterface;
28
use FacebookAds\Http\ResponseInterface;
29
use FacebookAds\Http\Util;
30
use FacebookAds\Object\AbstractObject;
31
32
class Cursor implements \Iterator, \Countable, \arrayaccess {
33
  /**
34
   * @var ResponseInterface
35
   */
36
  protected $response;
37
38
  /**
39
   * @var Api
40
   */
41
  protected $api;
42
43
  /**
44
   * @var AbstractObject[]
45
   */
46
  protected $objects = array();
47
48
  /**
49
   * @var int|null
50
   */
51
  protected $indexLeft;
52
53
  /**
54
   * @var int|null
55
   */
56
  protected $indexRight;
57
58
  /**
59
   * @var int|null
60
   */
61
  protected $position;
62
63
  /**
64
   * @var AbstractObject
65
   */
66
  protected $objectPrototype;
67
68
  /**
69
   * @var bool
70
   */
71
  protected static $defaultUseImplicitFetch = false;
72
73
  /**
74
   * @var bool
75
   */
76
  protected $useImplicitFectch;
77
78 24
  public function __construct(
79
    ResponseInterface $response,
80
    AbstractObject $object_prototype,
81
    Api $api = null) {
82 24
    $this->response = $response;
83 24
    $this->objectPrototype = $object_prototype;
84 24
    $this->api = $api !== null ? $api : Api::instance();
85 24
    $this->appendResponse($response);
86 18
  }
87
88
  /**
89
   * @param array $object_data
90
   * @return AbstractObject
91
   */
92 18
  protected function createObject(array $object_data) {
93 18
    $object = clone $this->objectPrototype;
94 18
    $object->setDataWithoutValidation($object_data);
95 18
    if ($object instanceof AbstractCrudObject) {
0 ignored issues
show
Bug introduced by
The class FacebookAds\AbstractCrudObject does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
96
      $object->setApi($this->api);
97
    }
98 18
    return $object;
99
  }
100
101
  /**
102
   * @param ResponseInterface $response
103
   * @return array
104
   * @throws \InvalidArgumentException
105
   */
106 24
  protected function assureResponseData(ResponseInterface $response) {
107 24
    $content = $response->getContent();
108
109
    // First, check if the content contains data
110 24
    if (isset($content['data']) && is_array($content['data'])) {
111 18
      $data = $content['data'];
112
113
      // If data is an object wrap the object into an array
114 18
      if ($this->isJsonObject($data)) {
115 10
        $data = array($data);
116 10
      }
117 18
      return $data;
118
    }
119
120
    // Second, check if the content contains special entries
121 6
    if (isset($content['targetingsentencelines'])) {
122
      return $content['targetingsentencelines'];
123
    }
124 6
    if (isset($content['adaccounts'])) {
125
      return $content['adaccounts'];
126
    }
127 6
    if (isset($content['users'])) {
128
      return $content['users'];
129
    }
130
131
    // Third, check if the content is an array of objects indexed by id
132 6
    $is_id_indexed_array = true;
133 6
    $objects = array();
134 6
    if (is_array($content) && count($content) >= 1) {
135 4
      foreach ($content as $key => $value) {
136 4
        if ($key === '__fb_trace_id__') {
137
          continue;
138
        }
139
140 4
        if ($value !== null &&
141 4
            $this->isJsonObject($value) &&
142 4
            isset($value['id']) &&
143 4
            $value['id'] !== null &&
144 4
            $value['id'] === $key) {
145
          $objects[] = $value;
146
        } else {
147 4
          $is_id_indexed_array = false;
148 4
          break;
149
        }
150 4
      }
151 4
    } else {
152 2
      $is_id_indexed_array = false;
153
    }
154 6
    if ($is_id_indexed_array) {
155
      return $objects;
156
    }
157
158 6
    throw new \InvalidArgumentException("Malformed response data");
159
  }
160
161 20
  private function isJsonObject($object) {
162 20
    if (!is_array($object)) {
163 2
      return false;
164
    }
165
166
    // A json object is represented by a map instead of a pure list
167 18
    return array_keys($object) !== range(0, count($object) - 1);
168
  }
169
170
  /**
171
   * @param ResponseInterface $response
172
   */
173 2
  protected function prependResponse(ResponseInterface $response) {
174 2
    $this->response = $response;
175 2
    $data = $this->assureResponseData($response);
176 2
    if (empty($data)) {
177
      return;
178
    }
179
180 2
    $left_index = $this->indexLeft;
181 2
    $count = count($data);
182 2
    $position = $count - 1;
183 2
    for ($i = $left_index - 1; $i >= $left_index - $count; $i--) {
184 2
      $this->objects[$i] = $this->createObject($data[$position--]);
185 2
      --$this->indexLeft;
186 2
    }
187 2
  }
188
189
  /**
190
   * @param ResponseInterface $response
191
   */
192 24
  protected function appendResponse(ResponseInterface $response) {
193 24
    $this->response = $response;
194 24
    $data = $this->assureResponseData($response);
195 18
    if (empty($data)) {
196
      return;
197
    }
198
199 18
    if ($this->indexRight === null) {
200 18
      $this->indexLeft = 0;
201 18
      $this->indexRight = -1;
202 18
      $this->position = 0;
203 18
    }
204
205 18
    $this->indexRight += count($data);
206
207 18
    foreach ($data as $object_data) {
208 18
      $this->objects[] = $this->createObject($object_data);
209 18
    }
210 18
  }
211
212
  /**
213
   * @return bool
214
   */
215 2
  public static function getDefaultUseImplicitFetch() {
216 2
    return static::$defaultUseImplicitFetch;
217
  }
218
219
  /**
220
   * @param bool $use_implicit_fectch
221
   */
222 2
  public static function setDefaultUseImplicitFetch($use_implicit_fectch) {
223 2
    static::$defaultUseImplicitFetch = $use_implicit_fectch;
224 2
  }
225
226
  /**
227
   * @return bool
228
   */
229 4
  public function getUseImplicitFetch() {
230 4
    return $this->useImplicitFectch !== null
231 4
      ? $this->useImplicitFectch
232 4
      : static::$defaultUseImplicitFetch;
233
  }
234
235
  /**
236
   * @param bool $use_implicit_fectch
237
   */
238 2
  public function setUseImplicitFetch($use_implicit_fectch) {
239 2
    $this->useImplicitFectch = $use_implicit_fectch;
240 2
  }
241
242
  /**
243
   * @return string|null
244
   */
245 8
  public function getBefore() {
246 8
    $content = $this->getLastResponse()->getContent();
247 8
    return isset($content['paging']['cursors']['before'])
248 8
      ? $content['paging']['cursors']['before']
249 8
      : null;
250
  }
251
252
  /**
253
   * @return string|null
254
   */
255 9
  public function getAfter() {
256 9
    $content = $this->getLastResponse()->getContent();
257 9
    return isset($content['paging']['cursors']['after'])
258 9
      ? $content['paging']['cursors']['after']
259 9
      : null;
260
  }
261
262
  /**
263
   * @return RequestInterface
264
   */
265 4
  protected function createUndirectionalizedRequest() {
266 4
    $request = $this->getLastResponse()->getRequest()->createClone();
267 4
    $params = $request->getQueryParams();
268 4
    if (array_key_exists('before', $params)) {
269 2
      unset($params['before']);
270 2
    }
271 4
    if (array_key_exists('after', $params)) {
272 2
      unset($params['after']);
273 2
    }
274
275 4
    return $request;
276
  }
277
278
  /**
279
   * @return string|null
280
   */
281 6
  public function getPrevious() {
282 6
    $content = $this->getLastResponse()->getContent();
283 6
    if (isset($content['paging']['previous'])) {
284 1
      return $content['paging']['previous'];
285
    }
286
287 6
    $before = $this->getBefore();
288 6
    if ($before !== null) {
289 1
      $request = $this->createUndirectionalizedRequest();
290 1
      $request->getQueryParams()->offsetSet('before', $before);
291 1
      return $request->getUrl();
292
    }
293
294 6
    return null;
295
  }
296
297
  /**
298
   * @return string|null
299
   */
300 10
  public function getNext() {
301 10
    $content = $this->getLastResponse()->getContent();
302 10
    if (isset($content['paging']['next'])) {
303 4
      return $content['paging']['next'];
304
    }
305
306 7
    $after = $this->getAfter();
307 7
    if ($after !== null) {
308 4
      $request = $this->createUndirectionalizedRequest();
309 4
      $request->getQueryParams()->offsetSet('after', $after);
310 4
      return $request->getUrl();
311
    }
312
313 4
    return null;
314
  }
315
316
  /**
317
   * @param string $url
318
   * @return RequestInterface
319
   */
320 8
  protected function createRequestFromUrl($url) {
321 8
    $components = parse_url($url);
322 8
    $request = $this->getLastResponse()->getRequest()->createClone();
323 8
    $request->setDomain($components['host']);
324 8
    $query = isset($components['query'])
325 8
      ? Util::parseUrlQuery($components['query'])
326 8
      : array();
327 8
    $request->getQueryParams()->enhance($query);
0 ignored issues
show
Unused Code introduced by
The call to the method FacebookAds\Http\Parameters::enhance() seems un-needed as the method has no side-effects.

PHP Analyzer performs a side-effects analysis of your code. A side-effect is basically anything that might be visible after the scope of the method is left.

Let’s take a look at an example:

class User
{
    private $email;

    public function getEmail()
    {
        return $this->email;
    }

    public function setEmail($email)
    {
        $this->email = $email;
    }
}

If we look at the getEmail() method, we can see that it has no side-effect. Whether you call this method or not, no future calls to other methods are affected by this. As such code as the following is useless:

$user = new User();
$user->getEmail(); // This line could safely be removed as it has no effect.

On the hand, if we look at the setEmail(), this method _has_ side-effects. In the following case, we could not remove the method call:

$user = new User();
$user->setEmail('email@domain'); // This line has a side-effect (it changes an
                                 // instance variable).
Loading history...
328
329 8
    return $request;
330
  }
331
332
  /**
333
   * @return RequestInterface|null
334
   */
335 6
  public function createBeforeRequest() {
336 6
    $url = $this->getPrevious();
337 6
    return $url !== null ? $this->createRequestFromUrl($url) : null;
338
  }
339
340
  /**
341
   * @return RequestInterface|null
342
   */
343 10
  public function createAfterRequest() {
344 10
    $url = $this->getNext();
345 10
    return $url !== null ? $this->createRequestFromUrl($url) : null;
346
  }
347
348 6
  public function fetchBefore() {
349 6
    $request = $this->createBeforeRequest();
350 6
    if (!$request) {
351 6
      return;
352
    }
353
354 2
    $this->prependResponse($request->execute());
355 2
  }
356
357 10
  public function fetchAfter() {
358 10
    $request = $this->createAfterRequest();
359 10
    if (!$request) {
360 4
      return;
361
    }
362
363 8
    $this->appendResponse($request->execute());
364 8
  }
365
366
  /**
367
   * @deprecated Use getArrayCopy()
368
   * @return AbstractObject[]
369
   */
370
  public function getObjects() {
371
    return $this->objects;
372
  }
373
374
  /**
375
   * @param bool $ksort
376
   * @return AbstractObject[]
377
   */
378 2
  public function getArrayCopy($ksort = false) {
379 2
    if ($ksort) {
380
      // Sort the main array to improve best case performance in future
381
      // invocations
382 2
      ksort($this->objects);
383 2
    }
384
385 2
    return $this->objects;
386
  }
387
388
  /**
389
   * @deprecated Use getLastResponse()
390
   * @return ResponseInterface
391
   */
392
  public function getResponse() {
393
    return $this->response;
394
  }
395
396
  /**
397
   * @return ResponseInterface
398
   */
399 12
  public function getLastResponse() {
400 12
    return $this->response;
401
  }
402
403
  /**
404
   * @return int
405
   */
406 8
  public function getIndexLeft() {
407 8
    return $this->indexLeft;
408
  }
409
410
  /**
411
   * @return int
412
   */
413 6
  public function getIndexRight() {
414 6
    return $this->indexRight;
415
  }
416
417 4
  public function rewind() {
418 4
    $this->position = $this->indexLeft;
419 4
  }
420
421 2
  public function end() {
422 2
    $this->position = $this->indexRight;
423 2
  }
424
425
  /**
426
   * @param int $position
427
   */
428 2
  public function seekTo($position) {
429 2
    $position = array_key_exists($position, $this->objects) ? $position : null;
430 2
    $this->position = $position;
431 2
  }
432
433
  /**
434
   * @return AbstractObject|bool
435
   */
436 2
  public function current() {
437 2
    return isset($this->objects[$this->position])
438 2
      ? $this->objects[$this->position]
439 2
      : false;
440
  }
441
442
  /**
443
   * @return int
444
   */
445 4
  public function key() {
446 4
    return $this->position;
447
  }
448
449 4
  public function prev() {
450 4
    if ($this->position == $this->getIndexLeft()) {
451 2
      if ($this->getUseImplicitFetch()) {
452 2
        $this->fetchBefore();
453 2
        if ($this->position == $this->getIndexLeft()) {
454 2
          $this->position = null;
455 2
        } else {
456 2
          --$this->position;
457
        }
458 2
      } else {
459 2
        $this->position = null;
460
      }
461 2
    } else {
462 4
      --$this->position;
463
    }
464 4
  }
465
466 6
  public function next() {
467 6
    if ($this->position == $this->getIndexRight()) {
468 4
      if ($this->getUseImplicitFetch()) {
469 2
        $this->fetchAfter();
470 2
        if ($this->position == $this->getIndexRight()) {
471 2
          $this->position = null;
472 2
        } else {
473 2
          ++$this->position;
474
        }
475 2
      } else {
476 2
        $this->position = null;
477
      }
478 4
    } else {
479 6
      ++$this->position;
480
    }
481 6
  }
482
483
  /**
484
   * @return bool
485
   */
486 4
  public function valid() {
487 4
    return isset($this->objects[$this->position]);
488
  }
489
490
  /**
491
   * @return int
492
   */
493 10
  public function count() {
494 10
    return count($this->objects);
495
  }
496
497
  /**
498
   * @param mixed $offset
499
   * @param mixed $value
500
   */
501 2
  public function offsetSet($offset, $value) {
502 2
    if ($offset === null) {
503 2
      $this->objects[] = $value;
504 2
    } else {
505 2
      $this->objects[$offset] = $value;
506
    }
507 2
  }
508
509
  /**
510
   * @param mixed $offset
511
   * @return bool
512
   */
513 2
  public function offsetExists($offset) {
514 2
    return isset($this->objects[$offset]);
515
  }
516
517
  /**
518
   * @param mixed $offset
519
   */
520 2
  public function offsetUnset($offset) {
521 2
    unset($this->objects[$offset]);
522 2
  }
523
524
  /**
525
   * @param mixed $offset
526
   * @return mixed
527
   */
528 2
  public function offsetGet($offset) {
529 2
    return isset($this->objects[$offset]) ? $this->objects[$offset] : null;
530
  }
531
}
532