Completed
Push — master ( 017182...7efb6d )
by Sergey
03:19 queued 45s
created

Request::exec()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 14
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 14
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 9
nc 2
nop 2
1
<?php
2
3
namespace seregazhuk\PinterestBot\Api;
4
5
use seregazhuk\PinterestBot\Helpers\UrlBuilder;
6
use seregazhuk\PinterestBot\Helpers\FileHelper;
7
use seregazhuk\PinterestBot\Helpers\CsrfParser;
8
use seregazhuk\PinterestBot\Exceptions\AuthFailed;
9
use seregazhuk\PinterestBot\Api\Contracts\HttpClient;
10
use seregazhuk\PinterestBot\Exceptions\InvalidRequest;
11
12
/**
13
 * Class Request.
14
 */
15
class Request
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
     * @var array|null
45
     */
46
    protected $lastError;
47
48
49
    /**
50
     * Common headers needed for every query.
51
     *
52
     * @var array
53
     */
54
    protected $requestHeaders = [
55
        'Accept: application/json, text/javascript, */*; q=0.01',
56
        'Accept-Language: en-US,en;q=0.5',
57
        'DNT: 1',
58
        'X-Pinterest-AppState: active',
59
        'X-NEW-APP: 1',
60
        'X-APP-VERSION: 04cf8cc',
61
        'X-Requested-With: XMLHttpRequest',
62
    ];
63
64
    /**
65
     * @param HttpClient $http
66
     */
67
    public function __construct(HttpClient $http)
68
    {
69
        $this->httpClient = $http;
70
        $this->loggedIn = false;
71
    }
72
73
    /**
74
     * @param string $pathToFile
75
     * @param string $url
76
     * @return string
77
     * @throws InvalidRequest
78
     */
79
    public function upload($pathToFile, $url)
80
    {
81
        $this->filePathToUpload = $pathToFile;
82
83
        return $this->exec($url);
84
    }
85
86
    /**
87
     * Executes request to Pinterest API.
88
     *
89
     * @param string $resourceUrl
90
     * @param string $postString
91
     *
92
     * @return Response
93
     */
94
    public function exec($resourceUrl, $postString = '')
95
    {
96
        $url = UrlBuilder::buildApiUrl($resourceUrl);
97
        $headers = $this->getHttpHeaders();
98
        $postString = $this->filePathToUpload ? $this->postFileData : $postString;
99
100
        $result = $this
101
            ->httpClient
102
            ->execute($url, $postString, $headers);
103
104
        $this->filePathToUpload = null;
105
106
        return $result;
107
    }
108
109
    /**
110
     * @return array
111
     */
112
    protected function getHttpHeaders()
113
    {
114
        $headers = $this->getDefaultHttpHeaders();
115
        if ($this->csrfToken == CsrfParser::DEFAULT_TOKEN) {
116
            $headers[] = 'Cookie: csrftoken=' . CsrfParser::DEFAULT_TOKEN . ';';
117
        }
118
119
        return $headers;
120
    }
121
122
    
123
    /**
124
     * Clear token information.
125
     *
126
     * @return $this
127
     */
128
    public function clearToken()
129
    {
130
        $this->csrfToken = CsrfParser::DEFAULT_TOKEN;
131
132
        return $this;
133
    }
134
135
    /**
136
     * Mark api as logged.
137
     * @throws AuthFailed
138
     */
139
    public function login()
140
    {
141
        $this->setTokenFromCookies();
142
        $this->loggedIn = true;
143
    }
144
145
    public function logout()
146
    {
147
        $this->clearToken();
148
        $this->loggedIn = false;
149
    }
150
151
    /**
152
     * Get log status.
153
     *
154
     * @return bool
155
     */
156
    public function isLoggedIn()
157
    {
158
        return $this->loggedIn;
159
    }
160
161
    /**
162
     * Create request string.
163
     *
164
     * @param array  $data
165
     * @param array  $bookmarks
166
     *
167
     * @return string
168
     */
169
    public static function createQuery(array $data = [], $bookmarks = [])
170
    {
171
        $data = ['options' => $data];
172
        $request = self::createRequestData($data, $bookmarks);
173
174
        return UrlBuilder::buildRequestString($request);
175
    }
176
177
    /**
178
     * @param array|object $data
179
     * @param array        $bookmarks
180
     *
181
     * @return array
182
     */
183
    public static function createRequestData(array $data = [], $bookmarks = [])
184
    {
185
        if (empty($data)) {
186
            $data = ['options' => []];
187
        }
188
189
        if (!empty($bookmarks)) {
190
            $data['options']['bookmarks'] = $bookmarks;
191
        }
192
193
        $data['context'] = new \stdClass();
194
195
        return [
196
            'source_url' => '',
197
            'data'       => json_encode($data),
198
        ];
199
    }
200
201
    public function setTokenFromCookies()
202
    {
203
        $this->csrfToken = $this->httpClient->getToken();
204
        if (empty($this->csrfToken)) {
205
            throw new AuthFailed('Cannot parse token from cookies.');
206
        }
207
208
        return $this;
209
    }
210
211
    /**
212
     * @return array
213
     */
214
    protected function getDefaultHttpHeaders()
215
    {
216
        return array_merge(
217
            $this->requestHeaders,
218
            $this->getContentTypeHeader(),
219
            [
220
                'Host: ' . UrlBuilder::HOST,
221
                'X-CSRFToken: ' . $this->csrfToken
222
            ]
223
        );
224
    }
225
226
    /**
227
     * If we are uploading file, we should build boundary form data. Otherwise
228
     * it is simple urlencoded form.
229
     *
230
     * @return array
231
     */
232
    protected function getContentTypeHeader()
233
    {
234
        return $this->filePathToUpload ?
235
            $this->makeHeadersForUpload() :
236
            ['Content-Type: application/x-www-form-urlencoded; charset=UTF-8;'];
237
    }
238
239
    /**
240
     * @param string $delimiter
241
     * @return $this
242
     */
243
    protected function buildFilePostData($delimiter)
244
    {
245
        $data = "--$delimiter\r\n";
246
        $data .= 'Content-Disposition: form-data; name="img"; filename="' . basename($this->filePathToUpload) . '"' . "\r\n";
247
        $data .= 'Content-Type: ' . FileHelper::getMimeType($this->filePathToUpload) . "\r\n\r\n";
248
        $data .= file_get_contents($this->filePathToUpload) . "\r\n";
249
        $data .= "--$delimiter--\r\n";
250
251
        $this->postFileData = $data;
252
253
        return $this;
254
    }
255
256
    /**
257
     * @return array
258
     */
259
    protected function makeHeadersForUpload()
260
    {
261
        $delimiter = '-------------' . uniqid();
262
        $this->buildFilePostData($delimiter);
263
264
        return [
265
            'Content-Type: multipart/form-data; boundary=' . $delimiter,
266
            'Content-Length: ' . strlen($this->postFileData)
267
        ];
268
    }
269
270
    /**
271
     * @return array|null
272
     */
273
    public function getLastError()
274
    {
275
        return $this->lastError;
276
    }
277
278
    /**
279
     * @return HttpClient
280
     */
281
    public function getHttpClient()
282
    {
283
        return $this->httpClient;
284
    }
285
}
286