Completed
Push — master ( 46628b...2ed37b )
by Rémi
04:03
created

TwitterApiClient::setRateLimit()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 3
cts 3
cp 1
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 2
crap 1
1
<?php
2
3
namespace Twitter\API\REST\Client;
4
5
use Psr\Log\LoggerAwareInterface;
6
use Psr\Log\LoggerAwareTrait;
7
use Psr\Log\NullLogger;
8
use Twitter\API\REST\TwitterApiGateway;
9
use Twitter\API\REST\TwitterClient;
10
use Twitter\Object\Tweet;
11
use Twitter\Object\TwitterDirectMessage;
12
use Twitter\Object\TwitterUser;
13
use Twitter\Serializer\TweetSerializer;
14
use Twitter\Serializer\TwitterDirectMessageSerializer;
15
use Twitter\Serializer\TwitterUserSerializer;
16
use Twitter\API\Exception\TwitterException;
17
use Twitter\API\Exception\TwitterRateLimitException;
18
use Twitter\API\REST\DTO\DeleteDirectMessageParameters;
19
use Twitter\API\REST\DTO\DeleteTweetParameters;
20
use Twitter\API\REST\DTO\DirectMessageParameters;
21
use Twitter\API\REST\DTO\FollowParameters;
22
use Twitter\API\REST\DTO\TweetParameters;
23
use Twitter\API\REST\DTO\UserIdentifier;
24
use Twitter\API\REST\Query\DirectMessage\DirectMessageQuery;
25
use Twitter\API\REST\Query\DirectMessage\SentDirectMessageQuery;
26
use Twitter\API\REST\Query\Friends\FriendsListQuery;
27
use Twitter\API\REST\Query\Tweet\MentionsTimelineQuery;
28
use Twitter\API\REST\Query\Tweet\UserTimelineQuery;
29
use Twitter\API\REST\Query\User\UserInformationQuery;
30
use Twitter\API\REST\Response\ApiRate;
31
use Twitter\API\REST\Response\ApiResponse;
32
33
class TwitterApiClient implements TwitterClient, LoggerAwareInterface
34
{
35
    use LoggerAwareTrait;
36
37
    /** @var TwitterApiGateway */
38
    private $adapter;
39
40
    /** @var TwitterUserSerializer */
41
    private $userSerializer;
42
43
    /** @var TweetSerializer */
44
    private $tweetSerializer;
45
46
    /** @var TwitterDirectMessageSerializer */
47
    private $directMessageSerializer;
48
49
    /** @var ApiRate[] */
50
    private $rateLimits;
51
52
    /**
53
     * Constructor
54
     *
55
     * @param TwitterApiGateway              $adapter
56
     * @param TwitterUserSerializer          $userSerializer
57
     * @param TweetSerializer                $tweetSerializer
58
     * @param TwitterDirectMessageSerializer $directMessageSerializer
59
     */
60 54
    public function __construct(
61
        TwitterApiGateway $adapter,
62
        TwitterUserSerializer $userSerializer,
63
        TweetSerializer $tweetSerializer,
64
        TwitterDirectMessageSerializer  $directMessageSerializer
65
    ) {
66 54
        $this->adapter = $adapter;
67
68 54
        $this->tweetSerializer = $tweetSerializer;
69 54
        $this->directMessageSerializer = $directMessageSerializer;
70 54
        $this->userSerializer = $userSerializer;
71
72 54
        $this->rateLimits = [];
73
74 54
        $this->logger = new NullLogger();
75 54
    }
76
77
    /**
78
     * @param string $userName
79
     *
80
     * @return TwitterUser
81
     *
82
     * @throws TwitterException
83
     */
84 6
    public function getUser($userName)
85
    {
86 6
        $this->checkRate(self::GET_USER);
87
88 3
        $response = $this->adapter->getUserInformation(
89 3
            new UserInformationQuery(
90 3
                UserIdentifier::fromScreenName($userName),
91 1
                true
92 2
            )
93 2
        );
94
95 3
        $this->handleResponse(self::GET_USER, $response);
96
97 3
        return $this->userSerializer->unserialize($response->getContent());
0 ignored issues
show
Documentation introduced by
$response->getContent() is of type array|object|null, but the function expects a object<stdClass>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
98
    }
99
100
    /**
101
     * Gets the mention tweets with id between $from and $to
102
     *
103
     * @param  string $from
104
     * @param  string $to
105
     *
106
     * @return Tweet[]
107
     *
108
     * @throws TwitterException
109
     */
110 9
    public function getMentionsTweets($from = null, $to = null)
111
    {
112 9
        $this->logger->info('Retrieving tweets messages from ' . $from . ' to ' . $to);
113
114 9
        $this->checkRate(self::GET_MENTIONS);
115
116 9
        $tweets = [];
117 9
        $response = $this->adapter->statusesMentionsTimeLine(new MentionsTimelineQuery(200, $from, $to, true));
118
119 9
        $this->handleResponse(self::GET_MENTIONS, $response);
120
121 9
        foreach ($response->getContent() as $index => $obj) {
0 ignored issues
show
Bug introduced by
The expression $response->getContent() of type array|object|null is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
122 6
            $tweet = $this->tweetSerializer->unserialize($obj);
123 6
            $id = (string) $tweet->getId();
124 6
            $tweets[(int) $id] = $tweet;
125 6
        }
126
127 9
        if (!empty($tweets) && $from !== null) {
128 6
            $moreTweets = $this->getMentionsTweets($from, min(array_keys($tweets))-1);
129 6
            if (!empty($moreTweets)) {
130 3
                $tweets += $moreTweets;
131 2
            }
132 4
        }
133 9
        ksort($tweets);
134 9
        return $tweets;
135
    }
136
137
    /**
138
     * Gets the direct messages with id between $from and $to
139
     *
140
     * @param  string $from
141
     * @param  string $to
142
     *
143
     * @return TwitterDirectMessage[]
144
     *
145
     * @throws TwitterException
146
     */
147 9
    public function getDirectMessages($from = null, $to = null)
148
    {
149 9
        $this->logger->info('Retrieving direct messages from ' . $from . ' to ' . $to);
150
151 9
        $this->checkRate(self::GET_DIRECT_MESSAGES);
152
153 9
        $dms = [];
154 9
        $response = $this->adapter->directMessages(new DirectMessageQuery(200, $from, $to, true));
155
156 9
        $this->handleResponse(self::GET_DIRECT_MESSAGES, $response);
157
158 9
        foreach ($response->getContent() as $index => $obj) {
0 ignored issues
show
Bug introduced by
The expression $response->getContent() of type array|object|null is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
159 6
            $dm = $this->directMessageSerializer->unserialize($obj);
160 6
            $id = (string) $dm->getId();
161 6
            $dms[(int) $id] = $dm;
162 6
        }
163
164 9
        if (!empty($dms) && $from !== null) {
165 6
            $moreDms = $this->getDirectMessages($from, min(array_keys($dms))-1);
166 6
            if (!empty($moreDms)) {
167 3
                $dms += $moreDms;
168 2
            }
169 4
        }
170 9
        ksort($dms);
171 9
        return $dms;
172
    }
173
174
    /**
175
     * @param string $userName
176
     *
177
     * @return Tweet[]
178
     *
179
     * @throws TwitterException
180
     */
181 3
    public function getSentTweets($userName)
182
    {
183 3
        $this->checkRate(self::GET_SENT_TWEETS);
184
185 3
        $response = $this->adapter->statusesUserTimeLine(
186 3
            new UserTimelineQuery(
187 3
                UserIdentifier::fromScreenName($userName),
188 1
                200
189 2
            )
190 2
        );
191
192 3
        $this->handleResponse(self::GET_SENT_TWEETS, $response);
193
194 3
        $tweets = [];
195 3
        foreach ($response->getContent() as $index => $obj) {
0 ignored issues
show
Bug introduced by
The expression $response->getContent() of type array|object|null is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
196 3
            $tweet = $this->tweetSerializer->unserialize($obj);
197 3
            $id = (string) $tweet->getId();
198 3
            $tweets[(int) $id] = $tweet;
199 2
        }
200 3
        ksort($tweets);
201 3
        return $tweets;
202
    }
203
204
    /**
205
     * @return TwitterDirectMessage[]
206
     *
207
     * @throws TwitterException
208
     */
209 3
    public function getSentDirectMessages()
210
    {
211 3
        $this->checkRate(self::GET_SENT_DIRECT_MESSAGES);
212
213 3
        $response = $this->adapter->sentDirectMessages(new SentDirectMessageQuery(200));
214
215 3
        $this->handleResponse(self::GET_SENT_DIRECT_MESSAGES, $response);
216
217 3
        $dms = [];
218 3
        foreach ($response->getContent() as $index => $obj) {
0 ignored issues
show
Bug introduced by
The expression $response->getContent() of type array|object|null is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
219 3
            $dm = $this->directMessageSerializer->unserialize($obj);
220 3
            $id = (string) $dm->getId();
221 3
            $dms[(int) $id] = $dm;
222 2
        }
223 3
        ksort($dms);
224 3
        return $dms;
225
    }
226
227
    /**
228
     * @param TwitterUser $user
229
     *
230
     * @return TwitterUser[]
231
     *
232
     * @throws TwitterException
233
     */
234 3
    public function getFollowedUsers(TwitterUser $user)
235
    {
236 3
        $cursor = -1;
237 3
        $serializedUsers = [];
238
239 3
        while ($cursor !== 0) {
240 3
            $this->checkRate(self::GET_FOLLOWED_USERS);
241
242 3
            $response = $this->adapter->friends(
243 3
                new FriendsListQuery(
244 3
                    UserIdentifier::fromId($user->getId()),
245 3
                    200,
246 1
                    $cursor
247 2
                )
248 2
            );
249
250 3
            $this->handleResponse(self::GET_FOLLOWED_USERS, $response);
251
252 3
            $friendsResponse = $response->getContent();
253 3
            $serializedUsers = array_merge($serializedUsers, $friendsResponse->users);
254
255 3
            $cursor = $friendsResponse->next_cursor;
256 2
        }
257
258 3
        return array_map(function ($serializedUser) {
259 3
            return $this->userSerializer->unserialize($serializedUser);
260 3
        }, $serializedUsers);
261
    }
262
263
    /**
264
     * Sends a tweet
265
     *
266
     * @param  string $message
267
     * @param  Tweet $replyTo
268
     *
269
     * @throws TwitterException
270
     */
271 6
    public function sendTweet($message, Tweet $replyTo = null)
272
    {
273 6
        $this->checkRate(self::SEND_TWEET);
274
275 6
        $params =  new TweetParameters(
276 6
            $message,
277 6
            $replyTo !== null ? (int) ((string) $replyTo->getId()) : null
278 4
        );
279
280 6
        $response = $this->adapter->updateStatus($params);
281
282 6
        $this->handleResponse(self::SEND_TWEET, $response);
283 6
    }
284
285
    /**
286
     * Sends a direct message to $user
287
     *
288
     * @param  TwitterUser $user
289
     * @param  string      $message
290
     *
291
     * @throws TwitterException
292
     */
293 3
    public function sendDirectMessage(TwitterUser $user, $message)
294
    {
295 3
        $this->checkRate(self::SEND_DIRECT_MESSAGE);
296
297 3
        $params =  new DirectMessageParameters(
298 3
            UserIdentifier::fromId($user->getId()),
299 1
            $message
300 2
        );
301
302 3
        $response = $this->adapter->newDirectMessage($params);
303
304 3
        $this->handleResponse(self::SEND_DIRECT_MESSAGE, $response);
305 3
    }
306
307
    /**
308
     * @param Tweet $tweet
309
     *
310
     * @throws TwitterException
311
     */
312
    public function deleteTweet(Tweet $tweet)
313
    {
314
        $this->checkRate(self::DELETE_TWEET);
315
316
        $params = new DeleteTweetParameters((string) $tweet->getId());
317
318
        $response = $this->adapter->deleteStatus($params);
319
320
        $this->handleResponse(self::DELETE_TWEET, $response);
321
    }
322
323
    /**
324
     * @param TwitterDirectMessage $directMessage
325
     *
326
     * @throws TwitterException
327
     */
328
    public function deleteDirectMessage(TwitterDirectMessage $directMessage)
329
    {
330
        $this->checkRate(self::DELETE_DIRECT_MESSAGE);
331
332
        $params = new DeleteDirectMessageParameters((string) $directMessage->getId());
333
334
        $response = $this->adapter->deleteDirectMessage($params);
335
336
        $this->handleResponse(self::DELETE_DIRECT_MESSAGE, $response);
337
    }
338
339
    /**
340
     * Follow a $user
341
     *
342
     * @param  TwitterUser $user
343
     *
344
     * @throws TwitterException
345
     */
346 3
    public function follow(TwitterUser $user)
347
    {
348 3
        $this->checkRate(self::FOLLOW);
349
350 3
        $params =  new FollowParameters(UserIdentifier::fromId($user->getId()));
351
352 3
        $response = $this->adapter->createFriendship($params);
353
354 3
        $this->handleResponse(self::FOLLOW, $response);
355 3
    }
356
357
    /**
358
     * Unfollow a $user
359
     *
360
     * @param  TwitterUser $user
361
     *
362
     * @throws TwitterException
363
     */
364 3
    public function unfollow(TwitterUser $user)
365
    {
366 3
        $this->checkRate(self::UNFOLLOW);
367
368 3
        $params = UserIdentifier::fromId($user->getId());
369
370 3
        $response = $this->adapter->destroyFriendship($params);
371
372 3
        $this->handleResponse(self::UNFOLLOW, $response);
373 3
    }
374
375
    /**
376
     * @param string  $category
377
     * @param ApiRate $rate
378
     */
379 48
    public function setRateLimit($category, ApiRate $rate)
380
    {
381 48
        $this->rateLimits[$category] = $rate;
382 48
    }
383
384
    /**
385
     * @param string $category
386
     *
387
     * @throws TwitterRateLimitException
388
     */
389 48
    private function checkRate($category)
390
    {
391 48
        if (!array_key_exists($category, $this->rateLimits)) {
392 45
            return;
393
        }
394
395 15
        $rate = $this->rateLimits[$category];
396 15
        if (!$rate->canMakeAnotherCall()) {
397 3
            throw TwitterRateLimitException::create($category, $rate);
398
        }
399 12
    }
400
401
    /**
402
     * @param string      $category
403
     * @param ApiResponse $response
404
     */
405 45
    private function handleResponse($category, ApiResponse $response)
406
    {
407 45
        $this->setRateLimit($category, $response->getRate());
408 45
    }
409
}
410