Completed
Pull Request — master (#19)
by
unknown
05:55
created

Api   B

Complexity

Total Complexity 51

Size/Duplication

Total Lines 539
Duplicated Lines 18.18 %

Coupling/Cohesion

Components 1
Dependencies 10

Test Coverage

Coverage 67.44%

Importance

Changes 2
Bugs 0 Features 0
Metric Value
wmc 51
c 2
b 0
f 0
lcom 1
cbo 10
dl 98
loc 539
ccs 116
cts 172
cp 0.6744
rs 8.3206

27 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
A processResponse() 0 9 2
A fetchUser() 10 10 1
A fetchBoard() 10 10 1
A fetchMultipleBoards() 11 11 2
A fetchMultipleUsers() 10 10 1
A fetchMultiplePins() 11 11 2
A getUser() 0 10 2
A getBoard() 0 10 2
A updateBoard() 0 20 4
A getUserBoards() 0 6 1
A getUserPins() 0 6 1
A getCurrentUser() 0 6 1
A getUserFollowers() 0 6 1
A getUserFollowingBoards() 0 6 1
A getUserFollowing() 0 6 1
A getUserInterests() 0 6 1
A followUser() 16 16 2
A createBoard() 0 18 3
A deleteBoard() 10 10 2
A buildRequestForPagedList() 0 15 1
A execute() 0 14 3
A fetchPin() 10 10 1
C createPin() 0 31 7
A deletePin() 10 10 2
B getNextItems() 0 26 3
A getBoardPins() 0 11 2

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like Api often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Api, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/*
4
 * This file is part of the Pinterest PHP library.
5
 *
6
 * (c) Hans Ott <[email protected]>
7
 *
8
 * This source file is subject to the MIT license that is bundled
9
 * with this source code in the file LICENSE.md.
10
 *
11
 * Source: https://github.com/hansott/pinterest-php
12
 */
13
14
namespace Pinterest;
15
16
use Pinterest\Objects\Pin;
17
use Pinterest\Objects\User;
18
use Pinterest\Http\Request;
19
use Pinterest\Http\Response;
20
use Pinterest\Objects\Board;
21
use InvalidArgumentException;
22
use Pinterest\Objects\PagedList;
23
use Pinterest\Http\Exceptions\RateLimitedReached;
24
25
/**
26
 * The api client.
27
 *
28
 * @author Hans Ott <[email protected]>
29
 * @author Toon Daelman <[email protected]>
30
 */
31
class Api
32
{
33
    /**
34
     * The authentication client.
35
     *
36
     * @var Authentication
37
     */
38
    private $auth;
39
40
    /**
41
     * The constructor.
42
     *
43
     * @param Authentication $auth The authentication client.
44
     */
45 36
    public function __construct(Authentication $auth)
46
    {
47 36
        $this->auth = $auth;
48 36
    }
49
50
    /**
51
     * Processes a response.
52
     *
53
     * @param Response $response  The response object.
54
     * @param callable $processor The response processor.
55
     *
56
     * @return Response The response
57
     */
58 20
    private function processResponse(Response $response, $processor)
59
    {
60 20
        if ($response->ok()) {
61 20
            $result = $processor($response);
62 20
            $response->setResult($result);
63 10
        }
64
65 20
        return $response;
66
    }
67
68
    /**
69
     * Execute the given http request.
70
     *
71
     * @param Request       $request
72
     * @param callable|null $processor
73
     *
74
     * @throws RateLimitedReached
75
     *
76
     * @return Response The response
77
     */
78 22
    public function execute(Request $request, $processor = null)
79 1
    {
80 22
        $response = $this->auth->execute($request);
81
82 22
        if ($response->rateLimited()) {
83
            throw new RateLimitedReached($response);
84
        }
85
86 22
        if (is_callable($processor)) {
87 20
            $response = $this->processResponse($response, $processor);
88 10
        }
89
90 22
        return $response;
91
    }
92
93
    /**
94
     * Fetch a single user and processes the response.
95
     *
96
     * @param Request $request
97
     *
98
     * @return Response The response
99
     */
100 4 View Code Duplication
    private function fetchUser(Request $request)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
101
    {
102 4
        $request->setFields(User::fields());
103
104
        return $this->execute($request, function (Response $response) {
105 4
            $mapper = new Mapper(new User());
106
107 4
            return $mapper->toSingle($response);
108 4
        });
109
    }
110
111
    /**
112
     * Fetch a single board and processes the response.
113
     *
114
     * @param Request $request
115
     *
116
     * @throws RateLimitedReached
117
     *
118
     * @return Response The response
119
     */
120 4 View Code Duplication
    private function fetchBoard(Request $request)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
121
    {
122 4
        $request->setFields(Board::fields());
123
124
        return $this->execute($request, function (Response $response) {
125 4
            $mapper = new Mapper(new Board());
126
127 4
            return $mapper->toSingle($response);
128 4
        });
129
    }
130
131
    /**
132
     * Fetch a single pin and processes the response.
133
     *
134
     * @param Request $request
135
     *
136
     * @throws RateLimitedReached
137
     *
138
     * @return Response The response
139
     */
140 View Code Duplication
    private function fetchPin(Request $request)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
141
    {
142
        $request->setFields(Pin::fields());
143
144
        return $this->execute($request, function (Response $response) {
145
            $mapper = new Mapper(new Pin());
146
147
            return $mapper->toSingle($response);
148
        });
149
    }
150
151
    /**
152
     * Fetch multiple boards and processes the response.
153
     *
154
     * @param Request  $request
155
     * @param string[] $fields
156
     *
157
     * @throws RateLimitedReached
158
     *
159
     * @return Response The response
160
     */
161 6 View Code Duplication
    private function fetchMultipleBoards(Request $request, array $fields = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
162
    {
163 6
        $fields = $fields ? $fields : Board::fields();
164 6
        $request->setFields($fields);
165
166
        return $this->execute($request, function (Response $response) {
167 6
            $mapper = new Mapper(new Board());
168
169 6
            return $mapper->toList($response);
170 6
        });
171
    }
172
173
    /**
174
     * Fetch multiple users and processes the response.
175
     *
176
     * @param Request $request
177
     *
178
     * @throws RateLimitedReached
179
     *
180
     * @return Response The response
181
     */
182 4 View Code Duplication
    private function fetchMultipleUsers(Request $request)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
183
    {
184 4
        $request->setFields(User::fields());
185
186
        return $this->execute($request, function (Response $response) {
187 4
            $mapper = new Mapper(new User());
188
189 4
            return $mapper->toList($response);
190 4
        });
191
    }
192
193
    /**
194
     * Fetches multiple pins and processes the response.
195
     *
196
     * @param Request $request
197
     * @param $fields array The fields to require.
198
     *
199
     * @throws RateLimitedReached
200
     *
201
     * @return Response The response
202
     */
203 2 View Code Duplication
    private function fetchMultiplePins(Request $request, array $fields = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
204
    {
205 2
        $fields = $fields ? $fields : Pin::fields();
206 2
        $request->setFields($fields);
207
208
        return $this->execute($request, function (Response $response) {
209 2
            $mapper = new Mapper(new Pin());
210
211 2
            return $mapper->toList($response);
212 2
        });
213
    }
214
215
    /**
216
     * Get a user.
217
     *
218
     * @param string $usernameOrId The username or identifier of the user.
219
     *
220
     * @return Response The response
221
     */
222 4
    public function getUser($usernameOrId)
223
    {
224 4
        if (empty($usernameOrId)) {
225 2
            throw new InvalidArgumentException('The username or id should not be empty.');
226
        }
227
228 2
        $request = new Request('GET', sprintf('users/%s/', $usernameOrId));
229
230 2
        return $this->fetchUser($request);
231
    }
232
233
    /**
234
     * Get a board.
235
     *
236
     * @param string $boardId The board id.
237
     *
238
     * @return Response The response
239
     */
240 2
    public function getBoard($boardId)
241
    {
242 2
        if (empty($boardId)) {
243
            throw new InvalidArgumentException('The board id should not be empty.');
244
        }
245
246 2
        $request = new Request('GET', sprintf('boards/%s/', $boardId));
247
248 2
        return $this->fetchBoard($request);
249
    }
250
251
    /**
252
     * Update a board.
253
     *
254
     * @param Board $board The updated board.
255
     *
256
     * @return Response The response
257
     */
258 2
    public function updateBoard(Board $board)
259
    {
260 2
        $params = array();
261
262 2
        if (empty($board->id)) {
263
            throw new InvalidArgumentException('The board id is required.');
264
        }
265
266 2
        if (!empty($board->name)) {
267 2
            $params['name'] = (string) $board->name;
268 1
        }
269
270 2
        if (!empty($board->description)) {
271 2
            $params['description'] = (string) $board->description;
272 1
        }
273
274 2
        $request = new Request('PATCH', sprintf('boards/%s/', $board->id), $params);
275
276 2
        return $this->fetchBoard($request);
277
    }
278
279
    /**
280
     * Get the boards of the authenticated user.
281
     *
282
     * @return Response The response
283
     */
284 2
    public function getUserBoards()
285
    {
286 2
        $request = new Request('GET', 'me/boards/');
287
288 2
        return $this->fetchMultipleBoards($request);
289
    }
290
291
    /**
292
     * Get the pins of the authenticated user.
293
     *
294
     * @return Response The response
295
     */
296 2
    public function getUserPins()
297
    {
298 2
        $request = new Request('GET', 'me/pins/');
299
300 2
        return $this->fetchMultiplePins($request);
301
    }
302
303
    /**
304
     * Get the authenticated user.
305
     *
306
     * @return Response The response
307
     */
308 2
    public function getCurrentUser()
309
    {
310 2
        $request = new Request('GET', 'me/');
311
312 2
        return $this->fetchUser($request);
313
    }
314
315
    /**
316
     * Get the followers of the authenticated user.
317
     *
318
     * @return Response The response
319
     */
320 2
    public function getUserFollowers()
321
    {
322 2
        $request = new Request('GET', 'me/followers/');
323
324 2
        return $this->fetchMultipleUsers($request);
325
    }
326
327
    /**
328
     * Get the boards that the authenticated user follows.
329
     *
330
     * @return Response The response
331
     */
332 2
    public function getUserFollowingBoards()
333
    {
334 2
        $request = new Request('GET', 'me/following/boards/');
335
336 2
        return $this->fetchMultipleBoards($request);
337
    }
338
339
    /**
340
     * Get the users that the authenticated user follows.
341
     *
342
     * @return Response The response
343
     */
344 2
    public function getUserFollowing()
345
    {
346 2
        $request = new Request('GET', 'me/following/users/');
347
348 2
        return $this->fetchMultipleUsers($request);
349
    }
350
351
    /**
352
     * Get the interests that the authenticated user follows.
353
     *
354
     * @link https://www.pinterest.com/explore/901179409185
355
     *
356
     * @return Response The response
357
     */
358 2
    public function getUserInterests()
359
    {
360 2
        $request = new Request('GET', 'me/following/interests/');
361
362 2
        return $this->fetchMultipleBoards($request, array('id', 'name'));
363
    }
364
365
    /**
366
     * Follow a user.
367
     *
368
     * @param string $username The username of the user to follow.
369
     *
370
     * @return Response The response
371
     */
372 2 View Code Duplication
    public function followUser($username)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
373
    {
374 2
        if (empty($username)) {
375 2
            throw new InvalidArgumentException('Username is required.');
376
        }
377
378 2
        $request = new Request(
379 2
            'POST',
380 2
            'me/following/users/',
381
            array(
382 2
                'user' => (string) $username,
383
            )
384 1
        );
385
386 2
        return $this->execute($request);
387
    }
388
389
    /**
390
     * Create a board.
391
     *
392
     * @param string $name        The board name.
393
     * @param string $description The board description.
394
     *
395
     * @return Response The response
396
     */
397 2
    public function createBoard($name, $description = null)
398
    {
399 2
        if (empty($name)) {
400
            throw new InvalidArgumentException('The name should not be empty.');
401
        }
402
403
        $params = array(
404 2
            'name' => (string) $name,
405 1
        );
406
407 2
        if (!empty($description)) {
408 2
            $params['description'] = (string) $description;
409 1
        }
410
411 2
        $request = new Request('POST', 'boards/', $params);
412
413 2
        return $this->fetchBoard($request);
414
    }
415
416
    /**
417
     * Delete a board.
418
     *
419
     * @param int $boardId The board id.
420
     *
421
     * @return Response The response
422
     */
423 2 View Code Duplication
    public function deleteBoard($boardId)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
424
    {
425 2
        if (empty($boardId)) {
426
            throw new InvalidArgumentException('The board id should not be empty.');
427
        }
428
429 2
        $request = new Request('DELETE', sprintf('boards/%d/', $boardId));
430
431 2
        return $this->execute($request);
432
    }
433
434
    /**
435
     * Create a pin on a board.
436
     *
437
     * @param string      $boardId The board id.
438
     * @param string      $note    The note.
439
     * @param Image       $image   The image.
440
     * @param string|null $link    The link (Optional).
441
     *
442
     * @return Response The response
443
     */
444 8
    public function createPin($boardId, $note, Image $image, $link = null)
445
    {
446 8
        if (empty($boardId)) {
447 8
            throw new InvalidArgumentException('The board id should not be empty.');
448
        }
449
450
        if (empty($note)) {
451
            throw new InvalidArgumentException('The note should not be empty.');
452
        }
453
454
        $params = array(
455
            'board' => $boardId,
456
            'note' => (string) $note,
457
        );
458
459
        if (!empty($link)) {
460
            $params['link'] = (string) $link;
461
        }
462
463
        $imageKey = $image->isUrl() ? 'image_url' : ($image->isBase64() ? 'image_base64' : 'image');
464
465
        if ($image->isFile()) {
466
            $params[$imageKey] = $image;
467
        } else {
468
            $params[$imageKey] = $image->getData();
469
        }
470
471
        $request = new Request('POST', 'pins/', $params);
472
473
        return $this->fetchPin($request);
474
    }
475
476
    /**
477
     * Delete a Pin.
478
     *
479
     * @param string $pinId The id of the pin to delete.
480
     *
481
     * @return Response The response
482
     */
483 View Code Duplication
    public function deletePin($pinId)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
484
    {
485
        if (empty($pinId)) {
486
            throw new InvalidArgumentException('The pin id should not be empty.');
487
        }
488
489
        $request = new Request('DELETE', sprintf('pins/%d/', $pinId));
490
491
        return $this->execute($request);
492
    }
493
494
    /**
495
     * Get the next items for a paged list.
496
     *
497
     * @param PagedList $pagedList
498
     *
499
     * @return Response The response
500
     */
501 2
    public function getNextItems(PagedList $pagedList)
502
    {
503 2
        if (!$pagedList->hasNext()) {
504 2
            throw new InvalidArgumentException('The list has no more items');
505
        }
506
507
        $items = $pagedList->items();
508
509
        if (empty($items)) {
510
            throw new InvalidArgumentException(
511
                'Unable to detect object type because the list contains no items'
512
            );
513
        }
514
515
        $item = reset($items);
516
        $objectClassName = get_class($item);
517
        $objectInstance = new $objectClassName();
518
519
        $request = $this->buildRequestForPagedList($pagedList);
520
521
        return $this->execute($request, function (Response $response) use ($objectInstance) {
522
            $mapper = new Mapper($objectInstance);
523
524
            return $mapper->toList($response);
525
        });
526
    }
527
528
    /**
529
     * Build a request to get the next items of a paged list.
530
     *
531
     * @param PagedList $pagedList
532
     *
533
     * @return Request
534
     */
535
    private function buildRequestForPagedList(PagedList $pagedList)
536
    {
537
        $nextItemsUri = $pagedList->getNextUrl();
538
539
        $params = array();
540
        $components = parse_url($nextItemsUri);
541
        parse_str($components['query'], $params);
542
543
        $path = $components['path'];
544
        $versionPath = sprintf('/%s/', Authentication::API_VERSION);
545
        $versionPathLength = strlen($versionPath);
546
        $path = substr($path, $versionPathLength);
547
548
        return new Request('GET', $path, $params);
0 ignored issues
show
Bug introduced by
It seems like $params can also be of type null; however, Pinterest\Http\Request::__construct() does only seem to accept array, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
549
    }
550
551
    /**
552
     * Get the pins of a board.
553
     *
554
     * @param string $boardId
555
     *
556
     * @return Response The response
557
     */
558 2
    public function getBoardPins($boardId)
559
    {
560 2
        if (empty($boardId)) {
561 2
            throw new InvalidArgumentException('The board id should not be empty.');
562
        }
563
564
        $endpoint = sprintf('boards/%s/pins/', $boardId);
565
        $request = new Request('GET', $endpoint);
566
567
        return $this->fetchMultiplePins($request);
568
    }
569
}
570