1
|
|
|
<?php |
2
|
|
|
namespace Aoe\Restler\System\RestApi; |
3
|
|
|
|
4
|
|
|
/*************************************************************** |
5
|
|
|
* Copyright notice |
6
|
|
|
* |
7
|
|
|
* (c) 2015 AOE GmbH <[email protected]> |
8
|
|
|
* |
9
|
|
|
* All rights reserved |
10
|
|
|
* |
11
|
|
|
* This script is part of the TYPO3 project. The TYPO3 project is |
12
|
|
|
* free software; you can redistribute it and/or modify |
13
|
|
|
* it under the terms of the GNU General Public License as published by |
14
|
|
|
* the Free Software Foundation; either version 3 of the License, or |
15
|
|
|
* (at your option) any later version. |
16
|
|
|
* |
17
|
|
|
* The GNU General Public License can be found at |
18
|
|
|
* http://www.gnu.org/copyleft/gpl.html. |
19
|
|
|
* |
20
|
|
|
* This script is distributed in the hope that it will be useful, |
21
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
22
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
23
|
|
|
* GNU General Public License for more details. |
24
|
|
|
* |
25
|
|
|
* This copyright notice MUST APPEAR in all copies of the script! |
26
|
|
|
***************************************************************/ |
27
|
|
|
|
28
|
|
|
use Aoe\Restler\System\TYPO3\Cache as Typo3Cache; |
29
|
|
|
use Luracast\Restler\Restler; |
30
|
|
|
use Luracast\Restler\RestException; |
31
|
|
|
use Luracast\Restler\Defaults; |
32
|
|
|
use Luracast\Restler\Format\JsonFormat; |
33
|
|
|
use TYPO3\CMS\Core\Utility\GeneralUtility; |
34
|
|
|
use TYPO3\CMS\Extbase\Object\ObjectManager; |
35
|
|
|
use Exception; |
36
|
|
|
use stdClass; |
37
|
|
|
|
38
|
|
|
/** |
39
|
|
|
* This class represents a single REST-API-request, which can be called from PHP. |
40
|
|
|
* For each REST-API request, we need a new object of this class, because restler stores some data in this object |
41
|
|
|
* |
42
|
|
|
* The logic/idea behind this class is: |
43
|
|
|
* 1. We override the data of $_GET, $_POST and $_SERVER and we override the REST-API-request-object (aka Restler-object) |
44
|
|
|
* ...because this data/object 'defines' the REST-API-request, which we want to call |
45
|
|
|
* 2. We call/start restler to handle the REST-API-request |
46
|
|
|
* 3. We return the result of the 'called' REST-API-request - instead of sending the result to the client |
47
|
|
|
*/ |
48
|
|
|
class RestApiRequest extends Restler |
49
|
|
|
{ |
50
|
|
|
/** |
51
|
|
|
* store data from $_GET in this property |
52
|
|
|
* |
53
|
|
|
* Attention: |
54
|
|
|
* This property must be static, because it can happen, that some REST-API-calls |
55
|
|
|
* will be called recursive, so we MUST store the 'really original' data |
56
|
|
|
* |
57
|
|
|
* @var array |
58
|
|
|
*/ |
59
|
|
|
private static $originalGetVars; |
60
|
|
|
/** |
61
|
|
|
* store data from $_POST in this property |
62
|
|
|
* |
63
|
|
|
* Attention: |
64
|
|
|
* This property must be static, because it can happen, that some REST-API-calls |
65
|
|
|
* will be called recursive, so we MUST store the 'really original' data |
66
|
|
|
* |
67
|
|
|
* @var array |
68
|
|
|
*/ |
69
|
|
|
private static $originalPostVars; |
70
|
|
|
/** |
71
|
|
|
* store data from $_SERVER in this property |
72
|
|
|
* |
73
|
|
|
* Attention: |
74
|
|
|
* This property must be static, because it can happen, that some REST-API-calls |
75
|
|
|
* will be called recursive, so we MUST store the 'really original' data |
76
|
|
|
* |
77
|
|
|
* @var array |
78
|
|
|
*/ |
79
|
|
|
private static $originalServerSettings; |
80
|
|
|
|
81
|
|
|
/** |
82
|
|
|
* @var array |
83
|
|
|
*/ |
84
|
|
|
private $restApiGetData; |
85
|
|
|
/** |
86
|
|
|
* @var array |
87
|
|
|
*/ |
88
|
|
|
private $restApiPostData; |
89
|
|
|
/** |
90
|
|
|
* This property defines the request-uri (without GET-params, e.g. '/api/products/320'), which should be called |
91
|
|
|
* |
92
|
|
|
* @var string |
93
|
|
|
*/ |
94
|
|
|
private $restApiRequestUri; |
95
|
|
|
/** |
96
|
|
|
* This property defines the request-method (e.g. 'GET', 'POST', 'PUT' or 'DELETE'), which should be used while calling the rest-api |
97
|
|
|
* |
98
|
|
|
* @var string |
99
|
|
|
*/ |
100
|
|
|
private $restApiRequestMethod; |
101
|
|
|
/** |
102
|
|
|
* @var RestApiRequestScope |
103
|
|
|
*/ |
104
|
|
|
private $restApiRequestScope; |
105
|
|
|
/** |
106
|
|
|
* @var Typo3Cache |
107
|
|
|
*/ |
108
|
|
|
private $typo3Cache; |
109
|
|
|
|
110
|
|
|
|
111
|
|
|
|
112
|
|
|
/***************************************************************************************************************************/ |
113
|
|
|
/***************************************************************************************************************************/ |
114
|
|
|
/* Block of methods, which does NOT override logic from parent-class *******************************************************/ |
115
|
|
|
/***************************************************************************************************************************/ |
116
|
|
|
/***************************************************************************************************************************/ |
117
|
|
|
/** |
118
|
|
|
* @param string $requestMethod |
119
|
|
|
* @param string $requestUri |
120
|
|
|
* @param array|stdClass $getData |
121
|
|
|
* @param array|stdClass $postData |
122
|
|
|
* @return mixed can be a primitive or array or object |
123
|
|
|
* @throws RestException |
124
|
|
|
*/ |
125
|
6 |
|
public function executeRestApiRequest($requestMethod, $requestUri, $getData = null, $postData = null) |
126
|
|
|
{ |
127
|
6 |
|
$this->restApiRequestMethod = $requestMethod; |
128
|
6 |
|
$this->restApiRequestUri = $requestUri; |
129
|
6 |
|
$this->restApiGetData = $this->convertDataToArray($getData); |
130
|
6 |
|
$this->restApiPostData = $this->convertDataToArray($postData); |
131
|
|
|
|
132
|
6 |
|
$this->storeOriginalRestApiRequest(); |
133
|
6 |
|
$this->overrideOriginalRestApiRequest(); |
134
|
|
|
|
135
|
|
|
try { |
136
|
6 |
|
$result = $this->handle(); |
137
|
2 |
|
$this->restoreOriginalRestApiRequest(); |
138
|
2 |
|
return $result; |
139
|
4 |
|
} catch (RestException $e) { |
|
|
|
|
140
|
2 |
|
$this->restoreOriginalRestApiRequest(); |
141
|
2 |
|
throw $e; |
142
|
2 |
|
} catch (Exception $e) { |
143
|
2 |
|
$this->restoreOriginalRestApiRequest(); |
144
|
2 |
|
throw new RestException(500, $e->getMessage(), array(), $e); |
145
|
|
|
} |
146
|
|
|
} |
147
|
|
|
|
148
|
|
|
/** |
149
|
|
|
* Return class, which can decode a JSON-string into a stdClass-object (instead of an array) |
150
|
|
|
* |
151
|
|
|
* @return RestApiJsonFormat |
152
|
|
|
*/ |
153
|
|
|
protected function getRestApiJsonFormat() |
154
|
|
|
{ |
155
|
|
|
$objectManager = GeneralUtility::makeInstance(ObjectManager::class); |
156
|
|
|
return $objectManager->get(RestApiJsonFormat::class); |
157
|
|
|
} |
158
|
|
|
|
159
|
|
|
/** |
160
|
|
|
* @param array|stdClass $data |
161
|
|
|
* @return array |
162
|
|
|
* @throws RestException |
163
|
|
|
*/ |
164
|
6 |
|
private function convertDataToArray($data) |
165
|
|
|
{ |
166
|
6 |
|
if ($data === null) { |
167
|
3 |
|
return array(); |
168
|
|
|
} |
169
|
3 |
|
if (is_array($data)) { |
170
|
3 |
|
return $data; |
171
|
|
|
} |
172
|
|
|
if ($data instanceof stdClass) { |
173
|
|
|
return json_decode(json_encode($data), true); // convert stdClass to array |
174
|
|
|
} |
175
|
|
|
throw new RestException(500, 'data must be type of null, array or stdClass'); |
176
|
|
|
} |
177
|
|
|
|
178
|
|
|
/** |
179
|
|
|
* @return string |
180
|
|
|
*/ |
181
|
|
|
private function handleRequestByTypo3Cache() |
182
|
|
|
{ |
183
|
|
|
$cacheEntry = $this->typo3Cache->getCacheEntry($this->url, $_GET); |
184
|
|
|
$this->responseData = $cacheEntry['responseData']; |
185
|
|
|
$this->responseFormat = new $cacheEntry['responseFormatClass'](); |
186
|
|
|
|
187
|
|
|
// send data to client |
188
|
|
|
if ($this->responseFormat instanceof JsonFormat) { |
|
|
|
|
189
|
|
|
// return stdClass-object (instead of an array) |
190
|
|
|
return $this->getRestApiJsonFormat()->decode($this->responseData); |
|
|
|
|
191
|
|
|
} |
192
|
|
|
return $this->responseFormat->decode($this->responseData); |
193
|
|
|
} |
194
|
|
|
|
195
|
|
|
/** |
196
|
|
|
* Override (the stored) data of $_GET, $_POST and $_SERVER (which are used in several restler-PHP-classes) and the original |
197
|
|
|
* REST-API-Request-object, because this data/object 'defines' the REST-API-request, which we want to call |
198
|
|
|
* |
199
|
|
|
* @return void |
200
|
|
|
*/ |
201
|
6 |
|
private function overrideOriginalRestApiRequest() |
202
|
|
|
{ |
203
|
6 |
|
$_GET = $this->restApiGetData; |
204
|
6 |
|
$_POST = $this->restApiPostData; |
205
|
6 |
|
$_SERVER['REQUEST_METHOD'] = $this->restApiRequestMethod; |
206
|
6 |
|
$_SERVER['REQUEST_URI'] = $this->restApiRequestUri; |
207
|
|
|
|
208
|
6 |
|
if ($this->restApiRequestMethod !== 'POST' && $this->restApiRequestMethod !== 'PUT') { |
209
|
|
|
// content-type and content-length should only exist when request-method is |
210
|
|
|
// POST or PUT (because in this case there can be the request-data in the body) |
211
|
6 |
|
if (array_key_exists('CONTENT_TYPE', $_SERVER)) { |
212
|
|
|
unset($_SERVER['CONTENT_TYPE']); |
213
|
|
|
} |
214
|
6 |
|
if (array_key_exists('HTTP_CONTENT_TYPE', $_SERVER)) { |
215
|
|
|
unset($_SERVER['HTTP_CONTENT_TYPE']); |
216
|
|
|
} |
217
|
6 |
|
if (array_key_exists('CONTENT_LENGTH', $_SERVER)) { |
218
|
|
|
unset($_SERVER['CONTENT_LENGTH']); |
219
|
|
|
} |
220
|
|
|
} |
221
|
|
|
|
222
|
6 |
|
$this->restApiRequestScope->overrideOriginalRestApiRequest($this); |
223
|
6 |
|
$this->restApiRequestScope->removeRestApiAuthenticationObjects(); |
224
|
|
|
|
225
|
|
|
/** |
226
|
|
|
* add all authentication-classes: |
227
|
|
|
* - we must add all authentication-classes, because the authentication-classes are stored in this object |
228
|
|
|
* - we don't must add all REST-API-classes, because the REST-API-classes are not stored in this object |
229
|
|
|
*/ |
230
|
6 |
|
$this->authClasses = $this->restApiRequestScope->getOriginalRestApiRequest()->_authClasses; |
231
|
6 |
|
} |
232
|
|
|
|
233
|
|
|
/** |
234
|
|
|
* Restore (the overridden) data of $_GET, $_POST and $_SERVER and the original REST-API-request-object |
235
|
|
|
* @return void |
236
|
|
|
*/ |
237
|
6 |
|
private function restoreOriginalRestApiRequest() |
238
|
|
|
{ |
239
|
6 |
|
$_GET = self::$originalGetVars; |
240
|
6 |
|
$_POST = self::$originalPostVars; |
241
|
6 |
|
$_SERVER = self::$originalServerSettings; |
242
|
6 |
|
$this->restApiRequestScope->restoreOriginalRestApiRequest(); |
243
|
6 |
|
$this->restApiRequestScope->restoreOriginalRestApiAuthenticationObjects(); |
244
|
6 |
|
} |
245
|
|
|
|
246
|
|
|
/** |
247
|
|
|
* Store (the original) data of $_GET, $_POST and $_SERVER and the original REST-API-request-object |
248
|
|
|
* @return void |
249
|
|
|
*/ |
250
|
6 |
|
private function storeOriginalRestApiRequest() |
251
|
|
|
{ |
252
|
6 |
|
if (false === isset(self::$originalServerSettings)) { |
253
|
1 |
|
self::$originalGetVars = $_GET; |
254
|
1 |
|
self::$originalPostVars = $_POST; |
255
|
1 |
|
self::$originalServerSettings = $_SERVER; |
256
|
1 |
|
$this->restApiRequestScope->storeOriginalRestApiRequest(); |
257
|
1 |
|
$this->restApiRequestScope->storeOriginalRestApiAuthenticationObjects(); |
258
|
|
|
} |
259
|
6 |
|
} |
260
|
|
|
|
261
|
|
|
|
262
|
|
|
|
263
|
|
|
/***************************************************************************************************************************/ |
264
|
|
|
/***************************************************************************************************************************/ |
265
|
|
|
/* Block of methods, which MUST be overriden from parent-class (otherwise this class can not work) *************************/ |
266
|
|
|
/***************************************************************************************************************************/ |
267
|
|
|
/***************************************************************************************************************************/ |
268
|
|
|
/** |
269
|
|
|
* Override parent method...because we don't want to call it! |
270
|
|
|
* The original method would set some properties (e.g. set this object into static properties of global classes) |
271
|
|
|
* |
272
|
|
|
* @param RestApiRequestScope $restApiRequestScope |
273
|
|
|
* @param Typo3Cache $typo3Cache |
274
|
|
|
*/ |
275
|
6 |
|
public function __construct(RestApiRequestScope $restApiRequestScope, Typo3Cache $typo3Cache) |
276
|
|
|
{ |
277
|
6 |
|
$this->restApiRequestScope = $restApiRequestScope; |
278
|
6 |
|
$this->typo3Cache = $typo3Cache; |
279
|
6 |
|
} |
280
|
|
|
|
281
|
|
|
/** |
282
|
|
|
* Override parent method...because we don't want to call it (the original method would cache the determined routes)! |
283
|
|
|
*/ |
284
|
|
|
public function __destruct() |
285
|
|
|
{ |
286
|
|
|
} |
287
|
|
|
|
288
|
|
|
/** |
289
|
|
|
* Override parent method...because we don't want to call it (the original method would send some headers to the client)! |
290
|
|
|
*/ |
291
|
|
|
public function composeHeaders(RestException $e = null) |
292
|
|
|
{ |
293
|
|
|
} |
294
|
|
|
|
295
|
|
|
/** |
296
|
|
|
* Override parent method...because we must return the request-data of THIS REST-API request! |
297
|
|
|
* The original method would return the request-data of the ORIGINAL called REST-API request |
298
|
|
|
* |
299
|
|
|
* @param boolean $includeQueryParameters |
300
|
|
|
* @return array |
301
|
|
|
*/ |
302
|
|
|
public function getRequestData($includeQueryParameters = true) |
303
|
|
|
{ |
304
|
|
|
$requestData = array(); |
305
|
|
|
if ($this->restApiRequestMethod == 'PUT' || $this->restApiRequestMethod == 'PATCH' || $this->restApiRequestMethod == 'POST') { |
306
|
|
|
$requestData = array_merge($this->restApiPostData, array(Defaults::$fullRequestDataName => $this->restApiPostData)); |
307
|
|
|
} |
308
|
|
|
|
309
|
|
|
if ($includeQueryParameters === true) { |
310
|
|
|
return $requestData + $this->restApiGetData; |
311
|
|
|
} |
312
|
|
|
return $requestData; |
313
|
|
|
} |
314
|
|
|
|
315
|
|
|
/** |
316
|
|
|
* Override parent method...because we must return the data of the REST-API request and we need NO exception-handling! |
317
|
|
|
* |
318
|
|
|
* @return mixed can be a primitive or array or object |
319
|
|
|
*/ |
320
|
|
|
public function handle() |
321
|
|
|
{ |
322
|
|
|
// get information about the REST-request |
323
|
|
|
$this->get(); |
324
|
|
|
|
325
|
|
|
if ($this->requestMethod === 'GET' && $this->typo3Cache->hasCacheEntry($this->url, $_GET)) { |
326
|
|
|
return $this->handleRequestByTypo3Cache(); |
327
|
|
|
} |
328
|
|
|
|
329
|
|
|
// if no cache exist: restler should handle the request |
330
|
|
|
if (Defaults::$useVendorMIMEVersioning) { |
331
|
|
|
$this->responseFormat = $this->negotiateResponseFormat(); |
332
|
|
|
} |
333
|
|
|
$this->route(); |
334
|
|
|
$this->negotiate(); |
335
|
|
|
$this->preAuthFilter(); |
336
|
|
|
$this->authenticate(); |
337
|
|
|
$this->postAuthFilter(); |
338
|
|
|
$this->validate(); |
339
|
|
|
$this->preCall(); |
340
|
|
|
$this->call(); |
341
|
|
|
$this->compose(); |
342
|
|
|
$this->postCall(); |
343
|
|
|
|
344
|
|
|
if ($this->responseFormat instanceof JsonFormat) { |
|
|
|
|
345
|
|
|
// return stdClass-object (instead of an array) |
346
|
|
|
return $this->getRestApiJsonFormat()->decode($this->responseData); |
347
|
|
|
} |
348
|
|
|
return $this->responseFormat->decode($this->responseData); |
349
|
|
|
} |
350
|
|
|
|
351
|
|
|
/** |
352
|
|
|
* override postCall so that we can cache response via TYPO3-caching-framework - if it's possible |
353
|
|
|
*/ |
354
|
|
|
protected function postCall() |
355
|
|
|
{ |
356
|
|
|
parent::postCall(); |
357
|
|
|
|
358
|
|
|
if ($this->typo3Cache->isResponseCacheableByTypo3Cache($this->requestMethod, $this->apiMethodInfo->metadata)) { |
359
|
|
|
$this->typo3Cache->cacheResponseByTypo3Cache( |
360
|
|
|
$this->url, |
361
|
|
|
$_GET, |
362
|
|
|
$this->apiMethodInfo->metadata, |
363
|
|
|
$this->responseData, |
364
|
|
|
get_class($this->responseFormat), |
365
|
|
|
array() // we don't know which headers would be 'normally' send - because this is an internal REST-API-call |
366
|
|
|
); |
367
|
|
|
} |
368
|
|
|
} |
369
|
|
|
} |
370
|
|
|
|
Scrutinizer analyzes your
composer.json
/composer.lock
file if available to determine the classes, and functions that are defined by your dependencies.It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.