Completed
Push — master ( e65e7e...962465 )
by Alberto
03:27
created

Chirp::request()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 7
rs 9.4285
cc 1
eloc 4
nc 1
nop 3
1
<?php
2
/**
3
 * This file is part of Chirp package
4
 *
5
 * Copyright (c) 2015 Alberto Pagliarini
6
 *
7
 * Licensed under the MIT license
8
 * https://github.com/batopa/chirp/blob/master/LICENSE
9
 */
10
namespace Bato\Chirp;
11
12
use MongoDB\Client;
13
use Abraham\TwitterOAuth\TwitterOAuth;
14
15
/**
16
 * Chirp Class
17
 *
18
 * Help to create a cache for Twitter using MongoDB
19
 *
20
 */
21
class Chirp
22
{
23
24
    /**
25
     * The Database object
26
     *
27
     * @var \MongoDB\Database
28
     */
29
    private $db = null;
30
31
    /**
32
     * The instance of TwitterOAuth
33
     *
34
     * @var \Abraham\TwitterOAuth\TwitterOAuth
35
     */
36
    private $twitter;
37
38
    /**
39
     * Constructor
40
     * Initialize TwitterAPIExchange and MongoDB connection
41
     *
42
     * $twitterAuthConf must contain
43
     * - consumer_key
44
     * - consumer_secret
45
     * - oauth_access_token
46
     * - oauth_access_token_secret
47
     *
48
     * $mongoConf must contain
49
     *  - db: the name of mongo database to use
50
     *
51
     *  and can contain
52
     * - uri
53
     * - uriOptions
54
     * - driverOptions
55
     *
56
     * used for MongoDB connection
57
     *
58
     * @see \MongoDB\Client for $mongoConf
59
     * @param array $twitterAuthConf
60
     * @param array $mongoConf
61
     */
62
    public function __construct(array $twitterAuthConf, array $mongoConf = array())
63
    {
64
        $twitterAuthConf += [
65
            'consumer_key' => null,
66
            'consumer_secret' => null,
67
            'oauth_access_token' => null,
68
            'oauth_access_token_secret' => null
69
        ];
70
        $mongoConf += [
71
            'uri' => 'mongodb://localhost:27017',
72
            'uriOptions' => [],
73
            'driverOptions' => [],
74
            'db' => ''
75
        ];
76
77
        $this->twitter = new TwitterOAuth(
78
            $twitterAuthConf['consumer_key'],
79
            $twitterAuthConf['consumer_secret'],
80
            $twitterAuthConf['oauth_access_token'],
81
            $twitterAuthConf['oauth_access_token_secret']
82
        );
83
        $this->twitter->setDecodeJsonAsArray(true);
84
85
        $client = new Client($mongoConf['uri'], $mongoConf['uriOptions'], $mongoConf['driverOptions']);
86
        $this->db = $client->selectDatabase($mongoConf['db']);
87
    }
88
89
    /**
90
     * Return TwitterOAuth instance
91
     *
92
     * @return \Abraham\TwitterOAuth\TwitterOAuth
93
     */
94
    public function getTwitterConnection()
95
    {
96
        return $this->twitter;
97
    }
98
99
    /**
100
     * Return the instance of MongoDB database
101
     *
102
     * @return \MongoDB\Database
103
     */
104
    public function getDb()
105
    {
106
        return $this->db;
107
    }
108
109
    /**
110
     * Starting from twitter endpoint return the related collection
111
     * It replaces "/" with "-" after removing trailing "/", for example:
112
     *
113
     * endpoint "statuses/user_timeline" corresponds to "statuses-user_timeline" collection
114
     *
115
     * @param string $endpoint [description]
116
     * @return \MongoDB\Collection
117
     */
118
    public function getCollection($endpoint)
119
    {
120
        $name = trim($endpoint, '/');
121
        $name = preg_replace('/\/+/', '-', $name);
122
        return $this->db->selectCollection($name);
123
    }
124
125
    /**
126
     * Perform a Twitter request
127
     *
128
     * @param string $endpoint the endpoint for example 'statuses/user_timeline'
129
     * @param array $query an array that specify query string to use with the endpoint
130
     * @param string $requestMethod the http request method
131
     * @return array
132
     */
133
    public function request($endpoint, $query = [], $requestMethod = 'get')
134
    {
135
        $response = $this->twitter
136
            ->{$requestMethod}($endpoint, $query);
137
138
        return $response;
139
    }
140
141
    /**
142
     * Read results from a collection (default self::collection)
143
     * using $filter to filter results
144
     *
145
     * If $options['limit'] = 1 return a \MongoDB\BSONDocument object
146
     * else return an array or a cursor depending from $options['returnType']
147
     *
148
     * @param string $endpoint
149
     * @param array $filter
150
     * @param array $options
151
     * @return object|array
152
     */
153
    public function read($endpoint, array $filter = [], array $options = [])
154
    {
155
        $options += ['returnType' => 'array'];
156
        $collection = $this->getCollection($endpoint);
157
        // delegate to MongoDB\Collection::findOne()
158
        if (isset($options['limit']) && $options['limit'] === 1) {
159
            return $collection->findOne($filter, $options);
160
        }
161
        $cursor = $collection->find($filter, $options);
162
        return ($options['returnType'] == 'array') ? iterator_to_array($cursor) : $cursor;
163
    }
164
165
    /**
166
     * Perform twitter request and save results in db.
167
     * Only requests returing tweets are persisted.
168
     *
169
     * Possible $options are:
170
     * - query: an array used for compose query string
171
     * - grep: a string to search in 'text' field. Result containing that string will be saved
172
     * - require: only results with that field will be saved
173
     *
174
     * @param string $endpoint the twitter endpoint for example 'statuses/user_timeline'
175
     * @param array $options
176
     * @return array
177
     */
178
    public function write($endpoint, array $options = [])
179
    {
180
        $options += [
181
            'query' => [],
182
            'grep' => '',
183
            'require' => ''
184
        ];
185
186
        $options['query'] = array_filter($options['query']);
187
188
        // use the API
189
        $response = $this->request($endpoint, $options['query']);
190
        if (empty($response)) {
191
            return ['saved' => [], 'read'=> []];
192
        }
193
194
        if (array_key_exists('errors', $response)) {
195
            return $response;
196
        }
197
198
        $collection = $this->getCollection($endpoint);
199
        $tweets = $this->filterToSave($response, $collection, $options);
200
        if (!empty($tweets)) {
201
            $collection->insertMany($tweets);
202
        }
203
204
        return [
205
            'saved' => $tweets,
206
            'read'=> $response
207
        ];
208
    }
209
210
    /**
211
     * Given an array of tweets and a collection
212
     * return the tweets that missing from the collection
213
     *
214
     * @see self::write() for $options
215
     * @param array $tweets
216
     * @param \MongoDB\Collection $collection
217
     * @param array $options
218
     * @return array
219
     */
220
    private function filterToSave(array $tweets, \MongoDB\Collection $collection, array $options)
221
    {
222
        $toSave = [];
223
        foreach ($tweets as $key => $tweet) {
224
            if (!isset($tweet['text']) || !isset($tweet['id_str'])) {
225
                continue;
226
            }
227
            if ($options['require'] && empty($tweet[$options['require']])) {
228
                continue;
229
            }
230
            if ($options['grep'] && stristr($tweet['text'], $options['grep']) === false) {
231
                continue;
232
            }
233
234
            $countTweets = $collection->count(['id_str' => $tweet['id_str']]);
235
            if ($countTweets === 0) {
236
                $toSave[] = $tweet;
237
            }
238
        }
239
        return $toSave;
240
    }
241
}
242