Completed
Pull Request — master (#126)
by Sergey
07:37
created

Request::makeHeadersForUpload()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 10
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

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