Completed
Pull Request — master (#291)
by Sergey
04:14 queued 01:32
created

Request::exec()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 16
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 16
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 10
nc 2
nop 2
1
<?php
2
3
namespace seregazhuk\PinterestBot\Api;
4
5
use seregazhuk\PinterestBot\Helpers\FileHelper;
6
use seregazhuk\PinterestBot\Helpers\UrlBuilder;
7
use seregazhuk\PinterestBot\Api\Contracts\HttpClient;
8
use seregazhuk\PinterestBot\Exceptions\InvalidRequest;
9
10
/**
11
 * Class Request.
12
 */
13
class Request
14
{
15
    const DEFAULT_TOKEN = '1234';
16
17
    /**
18
     * @var HttpClient
19
     */
20
    protected $httpClient;
21
22
    /**
23
     * @var bool
24
     */
25
    protected $loggedIn;
26
27
    /**
28
     *
29
     * @var string
30
     */
31
    protected $filePathToUpload;
32
33
    /**
34
     * @var string
35
     */
36
    protected $csrfToken = '';
37
38
    /**
39
     * @var string
40
     */
41
    protected $postFileData;
42
43
    /**
44
     * Common headers needed for every query.
45
     *
46
     * @var array
47
     */
48
    protected $requestHeaders = [
49
        'Accept: application/json, text/javascript, */*; q=0.01',
50
        'Accept-Language: en-US,en;q=0.5',
51
        'DNT: 1',
52
        'X-Pinterest-AppState: active',
53
        'X-NEW-APP: 1',
54
        'X-APP-VERSION: 831c928',
55
        'X-Requested-With: XMLHttpRequest',
56
        'X-Pinterest-AppState:active',
57
    ];
58
59
    /**
60
     * @param HttpClient $http
61
     */
62
    public function __construct(HttpClient $http)
63
    {
64
        $this->httpClient = $http;
65
        $this->loggedIn = false;
66
    }
67
68
    /**
69
     * @param string $pathToFile
70
     * @param string $url
71
     * @return string
72
     * @throws InvalidRequest
73
     */
74
    public function upload($pathToFile, $url)
75
    {
76
        $this->filePathToUpload = $pathToFile;
77
78
        return $this->exec($url);
79
    }
80
81
    /**
82
     * Executes request to Pinterest API.
83
     *
84
     * @param string $resourceUrl
85
     * @param string $postString
86
     *
87
     * @return string
88
     */
89
    public function exec($resourceUrl, $postString = '')
90
    {
91
        $url = UrlBuilder::buildApiUrl($resourceUrl);
92
        $headers = $this->getHttpHeaders();
93
        $postString = $this->filePathToUpload ? $this->postFileData : $postString;
94
95
        $result = $this
96
            ->httpClient
97
            ->execute($url, $postString, $headers);
98
99
        $this->setTokenFromCookies();
100
101
        $this->filePathToUpload = null;
102
103
        return $result;
104
    }
105
106
    /**
107
     * @return array
108
     */
109
    protected function getHttpHeaders()
110
    {
111
        $headers = $this->getDefaultHttpHeaders();
112
        if ($this->csrfToken == self::DEFAULT_TOKEN) {
113
            $headers[] = 'Cookie: csrftoken=' . self::DEFAULT_TOKEN . ';';
114
        }
115
116
        return $headers;
117
    }
118
119
    
120
    /**
121
     * Clear token information.
122
     *
123
     * @return $this
124
     */
125
    protected function clearToken()
126
    {
127
        $this->csrfToken = self::DEFAULT_TOKEN;
128
129
        return $this;
130
    }
131
132
    /**
133
     * Load cookies for this username and check if it was logged in.
134
     * @param string $username
135
     * @return bool
136
     */
137
    public function autoLogin($username)
138
    {
139
        $this->loadCookiesFor($username);
140
141
        if (!$this->httpClient->cookie('_auth')) {
142
            return false;
143
        }
144
145
        $this->login();
146
147
        return true;
148
    }
149
150
    /**
151
     * @param string $username
152
     * @return $this
153
     */
154
    public function loadCookiesFor($username)
155
    {
156
        $this->httpClient->loadCookies($username);
157
158
        return $this;
159
    }
160
161
    /**
162
     * Mark client as logged.
163
     */
164
    public function login()
165
    {
166
        $this->setTokenFromCookies();
167
168
        if(!empty($this->csrfToken)) {
169
            $this->loggedIn = true;
170
        }
171
172
        return $this;
173
    }
174
175
    /**
176
     * Mark client as logged out.
177
     */
178
    public function logout()
179
    {
180
        $this->clearToken();
181
        $this->loggedIn = false;
182
    }
183
184
    /**
185
     * Get current auth status.
186
     *
187
     * @return bool
188
     */
189
    public function isLoggedIn()
190
    {
191
        return $this->loggedIn;
192
    }
193
194
    /**
195
     * Create request string.
196
     *
197
     * @param array $data
198
     * @param array|null $bookmarks
199
     * @return string
200
     */
201
    public static function createQuery(array $data = [], $bookmarks = null)
202
    {
203
        $data = empty($data) ? [] : $data;
204
205
        $bookmarks = is_array($bookmarks) ? $bookmarks : [];
206
207
        $request = self::createRequestData(
208
            ['options' => $data], $bookmarks
209
        );
210
211
        return UrlBuilder::buildRequestString($request);
212
    }
213
214
    /**
215
     * @param array|object $data
216
     * @param array $bookmarks
217
     * @return array
218
     */
219
    public static function createRequestData(array $data = [], $bookmarks = [])
220
    {
221
        $data = self::prepareArrayToJson($data);
222
223
        if (!empty($bookmarks)) {
224
            $data['options']['bookmarks'] = $bookmarks;
225
        }
226
227
        if (empty($data)) {
228
            $data = ['options' => new \stdClass()];
229
        }
230
231
        $data['context'] = new \stdClass();
232
233
        return [
234
            'source_url' => '',
235
            'data'       => json_encode($data),
236
        ];
237
    }
238
239
    /**
240
     * Cast all non-array values to strings
241
     *
242
     * @param $array
243
     * @return array
244
     */
245
    protected static function prepareArrayToJson(array $array)
246
    {
247
        $result = [];
248
        foreach ($array as $key => $value) {
249
            $result[$key] = is_array($value) ?
250
                self::prepareArrayToJson($value) :
251
                strval($value);
252
        }
253
254
        return $result;
255
    }
256
257
    /**
258
     * Trying to execGet csrf token from cookies.
259
     *
260
     * @return $this
261
     */
262
    protected function setTokenFromCookies()
263
    {
264
        if($token = $this->httpClient->cookie('csrftoken')) {
265
            $this->csrfToken = $token;
266
        }
267
268
        return $this;
269
    }
270
271
    /**
272
     * @return array
273
     */
274
    protected function getDefaultHttpHeaders()
275
    {
276
        return array_merge(
277
            $this->requestHeaders,
278
            $this->getContentTypeHeader(),
279
            [
280
                'Host: ' . UrlBuilder::HOST,
281
                'Origin: ' . UrlBuilder::URL_BASE,
282
                'X-CSRFToken: ' . $this->csrfToken
283
            ]
284
        );
285
    }
286
287
    /**
288
     * If we are uploading file, we should build boundary form data. Otherwise
289
     * it is simple urlencoded form.
290
     *
291
     * @return array
292
     */
293
    protected function getContentTypeHeader()
294
    {
295
        return $this->filePathToUpload ?
296
            $this->makeHeadersForUpload() :
297
            ['Content-Type: application/x-www-form-urlencoded; charset=UTF-8;'];
298
    }
299
300
    /**
301
     * @param string $delimiter
302
     * @return $this
303
     */
304
    protected function buildFilePostData($delimiter)
305
    {
306
        $data = "--$delimiter\r\n";
307
        $data .= 'Content-Disposition: form-data; name="img"; filename="' . basename($this->filePathToUpload) . '"' . "\r\n";
308
        $data .= 'Content-Type: ' . FileHelper::getMimeType($this->filePathToUpload) . "\r\n\r\n";
309
        $data .= file_get_contents($this->filePathToUpload) . "\r\n";
310
        $data .= "--$delimiter--\r\n";
311
312
        $this->postFileData = $data;
313
314
        return $this;
315
    }
316
317
    /**
318
     * @return array
319
     */
320
    protected function makeHeadersForUpload()
321
    {
322
        $delimiter = '-------------' . uniqid();
323
        $this->buildFilePostData($delimiter);
324
325
        return [
326
            'Content-Type: multipart/form-data; boundary=' . $delimiter,
327
            'Content-Length: ' . strlen($this->postFileData)
328
        ];
329
    }
330
331
    /**
332
     * @return HttpClient
333
     */
334
    public function getHttpClient()
335
    {
336
        return $this->httpClient;
337
    }
338
339
    /**
340
     * @return string
341
     */
342
    public function getCurrentUrl()
343
    {
344
        return $this->httpClient->getCurrentUrl();
345
    }
346
347
    /**
348
     * @return $this
349
     */
350
    public function dropCookies()
351
    {
352
        $this->httpClient->removeCookies();
353
        $this->clearToken();
354
355
        return $this;
356
    }
357
}
358