Completed
Pull Request — master (#134)
by Sergey
05:09 queued 02:01
created

Request::followMethodCall()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 18
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 18
rs 9.4285
cc 2
eloc 10
nc 2
nop 3
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
        'X-Pinterest-AppState: active',
70
        'X-NEW-APP: 1',
71
        'X-APP-VERSION: 04cf8cc',
72
        'X-Requested-With: XMLHttpRequest',
73
    ];
74
75
    /**
76
     * @var string
77
     */
78
    protected $postFileData;
79
80
    /**
81
     * @param HttpInterface $http
82
     */
83
    public function __construct(HttpInterface $http)
84
    {
85
        $this->http = $http;
86
        $this->loggedIn = false;
87
        $this->cookieJar = tempnam(sys_get_temp_dir(), self::COOKIE_NAME);
88
    }
89
90
    /**
91
     * @param string $pathToFile
92
     * @param string $url
93
     *
94
     * @return array
95
     */
96
    public function upload($pathToFile, $url)
97
    {
98
        $this->filePathToUpload = $pathToFile;
99
        return $this->exec($url);
100
    }
101
102
    /**
103
     * Executes request to Pinterest API.
104
     *
105
     * @param string $resourceUrl
106
     * @param string $postString
107
     *
108
     * @return array
109
     */
110
    public function exec($resourceUrl, $postString = '')
111
    {
112
        $url = UrlHelper::buildApiUrl($resourceUrl);
113
        $this->makeHttpOptions($postString);
114
        $res = $this->http->execute($url, $this->options);
115
116
        $this->filePathToUpload = null;
117
        return json_decode($res, true);
118
    }
119
120
    /**
121
     * Adds necessary curl options for query.
122
     *
123
     * @param string $postString POST query string
124
     *
125
     * @return $this
126
     */
127
    protected function makeHttpOptions($postString = '')
128
    {
129
        $this->setDefaultHttpOptions();
130
131
        if ($this->csrfToken == CsrfHelper::DEFAULT_TOKEN) {
132
            $this->options = $this->addDefaultCsrfInfo($this->options);
133
        }
134
135
        if (!empty($postString) || $this->filePathToUpload) {
136
            $this->options[CURLOPT_POST] = true;
137
            $this->options[CURLOPT_POSTFIELDS] = $this->filePathToUpload ? $this->postFileData : $postString;
138
        }
139
140
        return $this;
141
    }
142
143
    /**
144
     * @return array
145
     */
146
    protected function setDefaultHttpOptions()
147
    {
148
        $this->options = [
149
            CURLOPT_USERAGENT      => $this->userAgent,
150
            CURLOPT_RETURNTRANSFER => true,
151
            CURLOPT_SSL_VERIFYPEER => false,
152
            CURLOPT_FOLLOWLOCATION => true,
153
            CURLOPT_ENCODING       => 'gzip,deflate',
154
            CURLOPT_HTTPHEADER     => $this->getDefaultHttpHeaders(),
155
            CURLOPT_REFERER        => UrlHelper::URL_BASE,
156
            CURLOPT_COOKIEFILE     => $this->cookieJar,
157
            CURLOPT_COOKIEJAR      => $this->cookieJar,
158
        ];
159
    }
160
161
    /**
162
     * @param array $options
163
     *
164
     * @return mixed
165
     */
166
    protected function addDefaultCsrfInfo($options)
167
    {
168
        $options[CURLOPT_REFERER] = UrlHelper::URL_BASE;
169
        $options[CURLOPT_HTTPHEADER][] = CsrfHelper::getDefaultCookie();
170
171
        return $options;
172
    }
173
    
174
    /**
175
     * Clear token information.
176
     *
177
     * @return $this
178
     */
179
    public function clearToken()
180
    {
181
        $this->csrfToken = CsrfHelper::DEFAULT_TOKEN;
182
183
        return $this;
184
    }
185
186
    /**
187
     * Mark api as logged.
188
     * @return $this
189
     * @throws AuthException
190
     */
191
    public function login()
192
    {
193
        $this->setTokenFromCookies();
194
        $this->loggedIn = true;
195
    }
196
197
    public function logout()
198
    {
199
        $this->clearToken();
200
        $this->loggedIn = false;
201
    }
202
203
    /**
204
     * Get log status.
205
     *
206
     * @return bool
207
     */
208
    public function isLoggedIn()
209
    {
210
        return $this->loggedIn;
211
    }
212
213
    /**
214
     * @param $userAgent
215
     * @return $this
216
     */
217
    public function setUserAgent($userAgent)
218
    {
219
        if ($userAgent !== null) {
220
            $this->userAgent = $userAgent;
221
        }
222
223
        return $this;
224
    }
225
226
    /**
227
     * Create request string.
228
     *
229
     * @param array  $data
230
     * @param array  $bookmarks
231
     *
232
     * @return string
233
     */
234
    public static function createQuery(array $data = [], $bookmarks = [])
235
    {
236
        $data = ['options' => $data];
237
        $request = self::createRequestData($data, $bookmarks);
238
239
        return UrlHelper::buildRequestString($request);
240
    }
241
242
    /**
243
     * @param array|object $data
244
     * @param array        $bookmarks
245
     *
246
     * @return array
247
     */
248
    public static function createRequestData(array $data = [], $bookmarks = [])
249
    {
250
        if (empty($data)) {
251
            $data = ['options' => []];
252
        }
253
254
        if (!empty($bookmarks)) {
255
            $data['options']['bookmarks'] = $bookmarks;
256
        }
257
258
        $data['context'] = new \stdClass();
259
260
        return [
261
            'source_url' => '',
262
            'data'       => json_encode($data),
263
        ];
264
    }
265
266
    public function setTokenFromCookies()
267
    {
268
        $this->csrfToken = CsrfHelper::getTokenFromFile($this->cookieJar);
269
        if (empty($this->csrfToken)) {
270
            throw new AuthException('Cannot parse token from cookies.');
271
        }
272
273
        return $this;
274
    }
275
276
    /**
277
     * @return array
278
     */
279
    protected function getDefaultHttpHeaders()
280
    {
281
        return array_merge(
282
            $this->requestHeaders, $this->getContentTypeHeader(), [
283
                'Host: ' . UrlHelper::HOST,
284
                'X-CSRFToken: ' . $this->csrfToken
285
            ]
286
        );
287
    }
288
289
    /**
290
     * If we are uploading file, we should build boundary form data. Otherwise
291
     * it is simple urlencoded form.
292
     *
293
     * @return array
294
     */
295
    protected function getContentTypeHeader()
296
    {
297
        return $this->filePathToUpload ?
298
            $this->makeHeadersForUpload() :
299
            ['Content-Type: application/x-www-form-urlencoded; charset=UTF-8;'];
300
    }
301
302
    /**
303
     * @param string $delimiter
304
     * @return $this
305
     */
306
    protected function buildFilePostData($delimiter)
307
    {
308
        $data = "--$delimiter\r\n";
309
        $data .= 'Content-Disposition: form-data; name="img"; filename="' . basename($this->filePathToUpload) . '"' . "\r\n";
310
        $data .= 'Content-Type: ' . FileHelper::getMimeType($this->filePathToUpload) . "\r\n\r\n";
311
        $data .= file_get_contents($this->filePathToUpload) . "\r\n";
312
        $data .= "--$delimiter--\r\n";
313
314
        $this->postFileData = $data;
315
316
        return $this;
317
    }
318
319
    protected function makeHeadersForUpload()
320
    {
321
        $delimiter = '-------------' . uniqid();
322
        $this->buildFilePostData($delimiter);
323
324
        return [
325
            'Content-Type: multipart/form-data; boundary=' . $delimiter,
326
            'Content-Length: ' . strlen($this->postFileData)
327
        ];
328
    }
329
}
330