Completed
Pull Request — master (#125)
by Sergey
03:45
created

Request::setTokenFromCookies()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
c 1
b 0
f 1
dl 0
loc 9
rs 9.6666
cc 2
eloc 5
nc 2
nop 0
1
<?php
2
3
namespace seregazhuk\PinterestBot\Api;
4
5
use seregazhuk\PinterestBot\Helpers\UrlHelper;
6
use seregazhuk\PinterestBot\Helpers\FileHelper;
7
use seregazhuk\PinterestBot\Helpers\CsrfHelper;
8
use seregazhuk\PinterestBot\Contracts\HttpInterface;
9
use seregazhuk\PinterestBot\Exceptions\AuthException;
10
11
/**
12
 * Class Request.
13
 *
14
 * @property resource $ch
15
 * @property bool     $loggedIn
16
 * @property string   $userAgent
17
 * @property string   $csrfToken
18
 * @property string   $cookieJar
19
 */
20
class Request
21
{
22
    const COOKIE_NAME = 'pinterest_cookie';
23
24
    protected $userAgent = 'Mozilla/5.0 (X11; Linux x86_64; rv:31.0) Gecko/20100101 Firefox/31.0';
25
    /**
26
     * @var HttpInterface
27
     */
28
    protected $http;
29
    protected $loggedIn;
30
    protected $cookieJar;
31
    protected $options;
32
33
    /**
34
     *
35
     * @var string
36
     */
37
    protected $filePathToUpload;
38
39
    /**
40
     * @var string
41
     */
42
    protected $csrfToken = '';
43
44
    /**
45
     * Common headers needed for every query.
46
     *
47
     * @var array
48
     */
49
    protected $requestHeaders = [
50
        'Accept: application/json, text/javascript, */*; q=0.01',
51
        'Accept-Language: en-US,en;q=0.5',
52
        'DNT: 1',
53
        'Host: nl.pinterest.com',
54
        'X-Pinterest-AppState: active',
55
        'X-NEW-APP: 1',
56
        'X-APP-VERSION: 04cf8cc',
57
        'X-Requested-With: XMLHttpRequest',
58
    ];
59
60
    /**
61
     * @var string
62
     */
63
    protected $postFileData;
64
65
    /**
66
     * @param HttpInterface $http
67
     */
68
    public function __construct(HttpInterface $http)
69
    {
70
        $this->http = $http;
71
        $this->loggedIn = false;
72
        $this->cookieJar = tempnam(sys_get_temp_dir(), self::COOKIE_NAME);
73
    }
74
75
    /**
76
     * Executes api call for follow or unfollow user.
77
     *
78
     * @param int    $entityId
79
     * @param string $entityName
80
     * @param string $url
81
     *
82
     * @return array
83
     */
84
    public function followMethodCall($entityId, $entityName, $url)
85
    {
86
        $dataJson = [
87
            'options' => [
88
                $entityName => (string)$entityId,
89
            ],
90
            'context' => [],
91
        ];
92
93
        if ($entityName == 'interest_id') {
94
            $dataJson['options']['interest_list'] = 'favorited';
95
        }
96
97
        $post = ['data' => json_encode($dataJson, JSON_FORCE_OBJECT)];
98
        $postString = UrlHelper::buildRequestString($post);
99
100
        return $this->exec($url, $postString);
101
    }
102
103
    /**
104
     * @param string $pathToFile
105
     * @param string $url
106
     *
107
     * @return array
108
     */
109
    public function upload($pathToFile, $url)
110
    {
111
        $this->filePathToUpload = $pathToFile;
112
113
        return $this->exec($url);
114
    }
115
116
    /**
117
     * Executes request to Pinterest API.
118
     *
119
     * @param string $resourceUrl
120
     * @param string $postString
121
     *
122
     * @return array
123
     */
124
    public function exec($resourceUrl, $postString = '')
125
    {
126
        $url = UrlHelper::buildApiUrl($resourceUrl);        
127
        $this->makeHttpOptions($postString);
128
        $res = $this->http->execute($url, $this->options);
129
130
        $this->filePathToUpload = null;
131
        return json_decode($res, true);
132
    }
133
134
    /**
135
     * Adds necessary curl options for query.
136
     *
137
     * @param string $postString POST query string
138
     *
139
     * @return $this
140
     */
141
    protected function makeHttpOptions($postString = '')
142
    {
143
        $this->setDefaultHttpOptions();
144
145
        if ($this->csrfToken == CsrfHelper::DEFAULT_TOKEN) {
146
            $this->options = $this->addDefaultCsrfInfo($this->options);
147
        }
148
149
        if (!empty($postString) || $this->filePathToUpload) {
150
            $this->options[CURLOPT_POST] = true;
151
            $this->options[CURLOPT_POSTFIELDS] = $this->filePathToUpload ? $this->postFileData : $postString;
152
        }
153
154
        return $this;
155
    }
156
157
    /**
158
     * @return array
159
     */
160
    protected function setDefaultHttpOptions()
161
    {
162
        $this->options = [
163
            CURLOPT_USERAGENT      => $this->userAgent,
164
            CURLOPT_RETURNTRANSFER => true,
165
            CURLOPT_SSL_VERIFYPEER => false,
166
            CURLOPT_FOLLOWLOCATION => true,
167
            CURLOPT_ENCODING       => 'gzip,deflate',
168
            CURLOPT_HTTPHEADER     => $this->getDefaultHttpHeaders(),
169
            CURLOPT_REFERER        => UrlHelper::URL_BASE,
170
            CURLOPT_COOKIEFILE     => $this->cookieJar,
171
            CURLOPT_COOKIEJAR      => $this->cookieJar,
172
        ];
173
    }
174
175
    /**
176
     * @param array $options
177
     *
178
     * @return mixed
179
     */
180
    protected function addDefaultCsrfInfo($options)
181
    {
182
        $options[CURLOPT_REFERER] = UrlHelper::URL_BASE;
183
        $options[CURLOPT_HTTPHEADER][] = CsrfHelper::getDefaultCookie();
184
185
        return $options;
186
    }
187
    
188
    /**
189
     * Clear token information.
190
     *
191
     * @return $this
192
     */
193
    public function clearToken()
194
    {
195
        $this->csrfToken = CsrfHelper::DEFAULT_TOKEN;
196
197
        return $this;
198
    }
199
200
    /**
201
     * Mark api as logged.
202
     * @return $this
203
     * @throws AuthException
204
     */
205
    public function login()
206
    {
207
        $this->setTokenFromCookies();
208
        $this->loggedIn = true;
209
    }
210
211
    public function logout()
212
    {
213
        $this->clearToken();
214
        $this->loggedIn = false;
215
    }
216
217
    /**
218
     * Get log status.
219
     *
220
     * @return bool
221
     */
222
    public function isLoggedIn()
223
    {
224
        return $this->loggedIn;
225
    }
226
227
    /**
228
     * @param $userAgent
229
     * @return $this
230
     */
231
    public function setUserAgent($userAgent)
232
    {
233
        if ($userAgent !== null) {
234
            $this->userAgent = $userAgent;
235
        }
236
237
        return $this;
238
    }
239
240
    /**
241
     * Create request string.
242
     *
243
     * @param array  $data
244
     * @param array  $bookmarks
245
     *
246
     * @return string
247
     */
248
    public static function createQuery(array $data = [], $bookmarks = [])
249
    {
250
        $data = ['options' => $data];
251
        $request = self::createRequestData($data, $bookmarks);
252
253
        return UrlHelper::buildRequestString($request);
254
    }
255
256
    /**
257
     * @param array|object $data
258
     * @param array        $bookmarks
259
     *
260
     * @return array
261
     */
262
    public static function createRequestData(array $data = [], $bookmarks = [])
263
    {
264
        if (empty($data)) {
265
            $data = ['options' => []];
266
        }
267
268
        if (!empty($bookmarks)) {
269
            $data['options']['bookmarks'] = $bookmarks;
270
        }
271
272
        $data['context'] = new \stdClass();
273
274
        return [
275
            'source_url' => '',
276
            'data'       => json_encode($data),
277
        ];
278
    }
279
280
    public function setTokenFromCookies()
281
    {
282
        $this->csrfToken = CsrfHelper::getTokenFromFile($this->cookieJar);
283
        if (empty($this->csrfToken)) {
284
            throw new AuthException('Cannot parse token from cookies.');
285
        }
286
287
        return $this;
288
    }
289
290
    /**
291
     * @return array
292
     */
293
    protected function getDefaultHttpHeaders()
294
    {
295
        return array_merge(
296
            $this->requestHeaders, $this->getContentTypeHeader(), ['X-CSRFToken: ' . $this->csrfToken]
297
        );
298
    }
299
300
    /**
301
     * If we are uploading file, we should build boundary form data. Otherwise
302
     * it is simple urlencoded form.
303
     *
304
     * @return array
305
     */
306
    protected function getContentTypeHeader()
307
    {
308
        if ($this->filePathToUpload) {
309
            $delimiter = '-------------' . uniqid();
310
            $this->buildFilePostData($delimiter);
311
312
            return [
313
                'Content-Type: multipart/form-data; boundary=' . $delimiter,
314
                'Content-Length: ' . strlen($this->postFileData)
315
            ];
316
        }
317
318
        return ['Content-Type: application/x-www-form-urlencoded; charset=UTF-8;'];
319
    }
320
321
    /**
322
     * @param string $delimiter
323
     * @return $this
324
     */
325
    protected function buildFilePostData($delimiter)
326
    {
327
        $data = "--$delimiter\r\n";
328
        $data .= 'Content-Disposition: form-data; name="img"; filename="' . basename($this->filePathToUpload) . '"' . "\r\n";
329
        $data .= 'Content-Type: ' . FileHelper::getMimeType($this->filePathToUpload) . "\r\n\r\n";
330
        $data .= file_get_contents($this->filePathToUpload) . "\r\n";
331
        $data .= "--$delimiter--\r\n";
332
333
        $this->postFileData = $data;
334
335
        return $this;
336
    }
337
}
338