Cursor::assureResponseData()   C
last analyzed

Complexity

Conditions 17
Paths 11

Size

Total Lines 54
Code Lines 33

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 26
CRAP Score 21.9113

Importance

Changes 0
Metric Value
dl 0
loc 54
ccs 26
cts 35
cp 0.7429
rs 6.6619
c 0
b 0
f 0
cc 17
eloc 33
nc 11
nop 1
crap 21.9113

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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 $useImplicitFetch;
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 16
  protected function createObject(array $object_data) {
93 16
    $object = clone $this->objectPrototype;
94 16
    $object->setDataWithoutValidation($object_data);
95 16
    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 16
    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
        $data = array($data);
116
      }
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
    // Consider an empty array as not object
167 18
    if (empty($object)) {
168 10
      return false;
169
    }
170
171
    // A json object is represented by a map instead of a pure list
172 16
    return array_keys($object) !== range(0, count($object) - 1);
173
  }
174
175
  /**
176
   * @param ResponseInterface $response
177
   */
178 2
  protected function prependResponse(ResponseInterface $response) {
179 2
    $this->response = $response;
180 2
    $data = $this->assureResponseData($response);
181 2
    if (empty($data)) {
182 2
      return;
183
    }
184
185 2
    $left_index = $this->indexLeft;
186 2
    $count = count($data);
187 2
    $position = $count - 1;
188 2
    for ($i = $left_index - 1; $i >= $left_index - $count; $i--) {
189 2
      $this->objects[$i] = $this->createObject($data[$position--]);
190 2
      --$this->indexLeft;
191 2
    }
192 2
  }
193
194
  /**
195
   * @param ResponseInterface $response
196
   */
197 24
  protected function appendResponse(ResponseInterface $response) {
198 24
    $this->response = $response;
199 24
    $data = $this->assureResponseData($response);
200 18
    if (empty($data)) {
201 10
      return;
202
    }
203
204 16
    if ($this->indexRight === null) {
205 16
      $this->indexLeft = 0;
206 16
      $this->indexRight = -1;
207 16
      $this->position = 0;
208 16
    }
209
210 16
    $this->indexRight += count($data);
211
212 16
    foreach ($data as $object_data) {
213 16
      $this->objects[] = $this->createObject($object_data);
214 16
    }
215 16
  }
216
217
  /**
218
   * @return bool
219
   */
220 2
  public static function getDefaultUseImplicitFetch() {
221 2
    return static::$defaultUseImplicitFetch;
222
  }
223
224
  /**
225
   * @param bool $use_implicit_fetch
226
   */
227 2
  public static function setDefaultUseImplicitFetch($use_implicit_fetch) {
228 2
    static::$defaultUseImplicitFetch = $use_implicit_fetch;
229 2
  }
230
231
  /**
232
   * @return bool
233
   */
234 4
  public function getUseImplicitFetch() {
235 4
    return $this->useImplicitFetch !== null
236 4
      ? $this->useImplicitFetch
237 4
      : static::$defaultUseImplicitFetch;
238
  }
239
240
  /**
241
   * @param bool $use_implicit_fetch
242
   */
243 2
  public function setUseImplicitFetch($use_implicit_fetch) {
244 2
    $this->useImplicitFetch = $use_implicit_fetch;
245 2
  }
246
247
  /**
248
   * @return string|null
249
   */
250 7
  public function getBefore() {
251 7
    $content = $this->getLastResponse()->getContent();
252 7
    return isset($content['paging']['cursors']['before'])
253 7
      ? $content['paging']['cursors']['before']
254 7
      : null;
255
  }
256
257
  /**
258
   * @return string|null
259
   */
260 8
  public function getAfter() {
261 8
    $content = $this->getLastResponse()->getContent();
262 8
    return isset($content['paging']['cursors']['after'])
263 8
      ? $content['paging']['cursors']['after']
264 8
      : null;
265
  }
266
267
  /**
268
   * @return RequestInterface
269
   */
270 4
  protected function createUndirectionalizedRequest() {
271 4
    $request = $this->getLastResponse()->getRequest()->createClone();
272 4
    $params = $request->getQueryParams();
273 4
    if (array_key_exists('before', $params)) {
274 2
      unset($params['before']);
275 2
    }
276 4
    if (array_key_exists('after', $params)) {
277 2
      unset($params['after']);
278 2
    }
279
280 4
    return $request;
281
  }
282
283
  /**
284
   * @return string|null
285
   */
286 6
  public function getPrevious() {
287 6
    $content = $this->getLastResponse()->getContent();
288 6
    if (isset($content['paging']['previous'])) {
289 1
      return $content['paging']['previous'];
290
    }
291
292 5
    $before = $this->getBefore();
293 5
    if ($before !== null) {
294 1
      $request = $this->createUndirectionalizedRequest();
295 1
      $request->getQueryParams()->offsetSet('before', $before);
296 1
      return $request->getUrl();
297
    }
298
299 4
    return null;
300
  }
301
302
  /**
303
   * @return string|null
304
   */
305 10
  public function getNext() {
306 10
    $content = $this->getLastResponse()->getContent();
307 10
    if (isset($content['paging']['next'])) {
308 4
      return $content['paging']['next'];
309
    }
310
311 6
    $after = $this->getAfter();
312 6
    if ($after !== null) {
313 4
      $request = $this->createUndirectionalizedRequest();
314 4
      $request->getQueryParams()->offsetSet('after', $after);
315 4
      return $request->getUrl();
316
    }
317
318 2
    return null;
319
  }
320
321
  /**
322
   * @param string $url
323
   * @return RequestInterface
324
   */
325 8
  protected function createRequestFromUrl($url) {
326 8
    $components = parse_url($url);
327 8
    $request = $this->getLastResponse()->getRequest()->createClone();
328 8
    $request->setDomain($components['host']);
329 8
    $query = isset($components['query'])
330 8
      ? Util::parseUrlQuery($components['query'])
331 8
      : array();
332 8
    $request->getQueryParams()->enhance($query);
333
334 8
    return $request;
335
  }
336
337
  /**
338
   * @return RequestInterface|null
339
   */
340 6
  public function createBeforeRequest() {
341 6
    $url = $this->getPrevious();
342 6
    return $url !== null ? $this->createRequestFromUrl($url) : null;
343
  }
344
345
  /**
346
   * @return RequestInterface|null
347
   */
348 10
  public function createAfterRequest() {
349 10
    $url = $this->getNext();
350 10
    return $url !== null ? $this->createRequestFromUrl($url) : null;
351
  }
352
353 6
  public function fetchBefore() {
354 6
    $request = $this->createBeforeRequest();
355 6
    if (!$request) {
356 4
      return;
357
    }
358
359 2
    $this->prependResponse($request->execute());
360 2
  }
361
362 10
  public function fetchAfter() {
363 10
    $request = $this->createAfterRequest();
364 10
    if (!$request) {
365 2
      return;
366
    }
367
368 8
    $this->appendResponse($request->execute());
369 8
  }
370
371
  /**
372
   * @deprecated Use getArrayCopy()
373
   * @return AbstractObject[]
374
   */
375
  public function getObjects() {
376
    return $this->objects;
377
  }
378
379
  /**
380
   * @param bool $ksort
381
   * @return AbstractObject[]
382
   */
383 2
  public function getArrayCopy($ksort = false) {
384 2
    if ($ksort) {
385
      // Sort the main array to improve best case performance in future
386
      // invocations
387 2
      ksort($this->objects);
388 2
    }
389
390 2
    return $this->objects;
391
  }
392
393
  /**
394
   * @deprecated Use getLastResponse()
395
   * @return ResponseInterface
396
   */
397
  public function getResponse() {
398
    return $this->response;
399
  }
400
401
  /**
402
   * @return ResponseInterface
403
   */
404 12
  public function getLastResponse() {
405 12
    return $this->response;
406
  }
407
408
  /**
409
   * @return int
410
   */
411 8
  public function getIndexLeft() {
412 8
    return $this->indexLeft;
413
  }
414
415
  /**
416
   * @return int
417
   */
418 6
  public function getIndexRight() {
419 6
    return $this->indexRight;
420
  }
421
422 4
  public function rewind() {
423 4
    $this->position = $this->indexLeft;
424 4
  }
425
426 2
  public function end() {
427 2
    $this->position = $this->indexRight;
428 2
  }
429
430
  /**
431
   * @param int $position
432
   */
433 2
  public function seekTo($position) {
434 2
    $position = array_key_exists($position, $this->objects) ? $position : null;
435 2
    $this->position = $position;
436 2
  }
437
438
  /**
439
   * @return AbstractObject|bool
440
   */
441 2
  public function current() {
442 2
    return isset($this->objects[$this->position])
443 2
      ? $this->objects[$this->position]
444 2
      : false;
445
  }
446
447
  /**
448
   * @return int
449
   */
450 4
  public function key() {
451 4
    return $this->position;
452
  }
453
454 4
  public function prev() {
455 4
    if ($this->position == $this->getIndexLeft()) {
456 2
      if ($this->getUseImplicitFetch()) {
457 2
        $this->fetchBefore();
458 2
        if ($this->position == $this->getIndexLeft()) {
459 2
          $this->position = null;
460 2
        } else {
461 2
          --$this->position;
462
        }
463 2
      } else {
464 2
        $this->position = null;
465
      }
466 2
    } else {
467 4
      --$this->position;
468
    }
469 4
  }
470
471 6
  public function next() {
472 6
    if ($this->position == $this->getIndexRight()) {
473 4
      if ($this->getUseImplicitFetch()) {
474 2
        $this->fetchAfter();
475 2
        if ($this->position == $this->getIndexRight()) {
476 2
          $this->position = null;
477 2
        } else {
478 2
          ++$this->position;
479
        }
480 2
      } else {
481 2
        $this->position = null;
482
      }
483 4
    } else {
484 6
      ++$this->position;
485
    }
486 6
  }
487
488
  /**
489
   * @return bool
490
   */
491 4
  public function valid() {
492 4
    return isset($this->objects[$this->position]);
493
  }
494
495
  /**
496
   * @return int
497
   */
498 10
  public function count() {
499 10
    return count($this->objects);
500
  }
501
502
  /**
503
   * @param mixed $offset
504
   * @param mixed $value
505
   */
506 2
  public function offsetSet($offset, $value) {
507 2
    if ($offset === null) {
508 2
      $this->objects[] = $value;
509 2
    } else {
510 2
      $this->objects[$offset] = $value;
511
    }
512 2
  }
513
514
  /**
515
   * @param mixed $offset
516
   * @return bool
517
   */
518 2
  public function offsetExists($offset) {
519 2
    return isset($this->objects[$offset]);
520
  }
521
522
  /**
523
   * @param mixed $offset
524
   */
525 2
  public function offsetUnset($offset) {
526 2
    unset($this->objects[$offset]);
527 2
  }
528
529
  /**
530
   * @param mixed $offset
531
   * @return mixed
532
   */
533 2
  public function offsetGet($offset) {
534 2
    return isset($this->objects[$offset]) ? $this->objects[$offset] : null;
535
  }
536
}
537