1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace Aoe\Restler\System\Restler; |
4
|
|
|
|
5
|
|
|
/*************************************************************** |
6
|
|
|
* Copyright notice |
7
|
|
|
* |
8
|
|
|
* (c) 2024 AOE GmbH <[email protected]> |
9
|
|
|
* |
10
|
|
|
* All rights reserved |
11
|
|
|
* |
12
|
|
|
* This script is part of the TYPO3 project. The TYPO3 project is |
13
|
|
|
* free software; you can redistribute it and/or modify |
14
|
|
|
* it under the terms of the GNU General Public License as published by |
15
|
|
|
* the Free Software Foundation; either version 3 of the License, or |
16
|
|
|
* (at your option) any later version. |
17
|
|
|
* |
18
|
|
|
* The GNU General Public License can be found at |
19
|
|
|
* http://www.gnu.org/copyleft/gpl.html. |
20
|
|
|
* |
21
|
|
|
* This script is distributed in the hope that it will be useful, |
22
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
23
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
24
|
|
|
* GNU General Public License for more details. |
25
|
|
|
* |
26
|
|
|
* This copyright notice MUST APPEAR in all copies of the script! |
27
|
|
|
***************************************************************/ |
28
|
|
|
|
29
|
|
|
use Aoe\Restler\System\TYPO3\Cache; |
30
|
|
|
use Exception; |
31
|
|
|
use Luracast\Restler\Defaults; |
32
|
|
|
use Luracast\Restler\RestException; |
33
|
|
|
use Luracast\Restler\Restler; |
34
|
|
|
use Luracast\Restler\Scope; |
35
|
|
|
use Psr\Http\Message\ServerRequestInterface; |
36
|
|
|
use TYPO3\CMS\Core\Site\Entity\Site; |
37
|
|
|
|
38
|
|
|
class RestlerExtended extends Restler |
39
|
|
|
{ |
40
|
|
|
/***************************************************************************************************************************/ |
41
|
|
|
/***************************************************************************************************************************/ |
42
|
|
|
/* Block of methods, which MUST be overriden from parent-class (otherwise we can't use the TYPO3-caching-framework) ********/ |
43
|
|
|
/***************************************************************************************************************************/ |
44
|
|
|
/***************************************************************************************************************************/ |
45
|
|
|
/** |
46
|
|
|
* @param bool $productionMode When set to false, it will run in |
47
|
|
|
* debug mode and parse the class files |
48
|
|
|
* every time to map it to the URL |
49
|
|
|
* |
50
|
|
|
* @param bool $refreshCache will update the cache when set to true |
51
|
|
|
* @param ServerRequestInterface $request frontend request |
52
|
|
|
*/ |
53
|
|
|
public function __construct( |
54
|
|
|
private readonly Cache $typo3Cache, |
55
|
|
|
$productionMode = false, |
56
|
|
|
$refreshCache = false, |
57
|
|
|
private readonly ?ServerRequestInterface $request = null |
58
|
|
|
) { |
59
|
|
|
parent::__construct($productionMode, $refreshCache); |
60
|
|
|
|
61
|
|
|
if (interface_exists(\Psr\Http\Server\MiddlewareInterface::class)) { |
62
|
|
|
// restler uses echo;die otherwise and then Typo3 standard mechanisms will not be called |
63
|
|
|
Defaults::$returnResponse = true; |
64
|
|
|
} |
65
|
|
|
|
66
|
|
|
// adds format support for application/hal+json |
67
|
|
|
Scope::$classAliases['HalJsonFormat'] = \Aoe\Restler\System\Restler\Format\HalJsonFormat::class; |
68
|
|
|
$this->setSupportedFormats('HalJsonFormat'); |
69
|
|
|
|
70
|
|
|
// set pathes from request if present |
71
|
|
|
if ($this->request !== null) { |
72
|
|
|
$this->url = $this->getPath(); |
73
|
|
|
} |
74
|
|
|
} |
75
|
|
|
|
76
|
|
|
/** |
77
|
|
|
* Main function for processing the api request |
78
|
|
|
* and return the response |
79
|
|
|
*/ |
80
|
|
|
public function handle() |
81
|
|
|
{ |
82
|
|
|
try { |
83
|
|
|
// get information about the REST-request (this is required to check, if we can handle the REST-request by TYPO3-cache) |
84
|
|
|
$this->get(); |
85
|
|
|
} catch (RestException) { |
86
|
|
|
// Exception occurred (e.g. 'Error encoding/decoding JSON') during getting information about REST-request: |
87
|
|
|
// Let restler handle the error (e.g. that JSON could not be read) - and NOT the TYPO3-exception-handling! |
88
|
|
|
return parent::handle(); |
|
|
|
|
89
|
|
|
} |
90
|
|
|
|
91
|
|
|
if ($this->requestMethod === 'GET' && $this->typo3Cache->hasCacheEntry($this->url, $_GET)) { |
92
|
|
|
return $this->handleRequestByTypo3Cache(); |
93
|
|
|
} |
94
|
|
|
|
95
|
|
|
// if no cache exist: restler should handle the request |
96
|
|
|
return parent::handle(); |
|
|
|
|
97
|
|
|
} |
98
|
|
|
|
99
|
|
|
/** |
100
|
|
|
* Rewrap the not accessible private stream in a new one. |
101
|
|
|
* |
102
|
|
|
* @return bool|resource |
103
|
|
|
*/ |
104
|
|
|
public function getRequestStream() |
105
|
|
|
{ |
106
|
|
|
$stream = fopen('php://temp', 'wb+'); |
107
|
|
|
fwrite($stream, (string) $this->request->getBody()); |
|
|
|
|
108
|
|
|
fseek($stream, 0, SEEK_SET); |
109
|
|
|
|
110
|
|
|
return $stream; |
111
|
|
|
} |
112
|
|
|
|
113
|
|
|
/** |
114
|
|
|
* Determine path (and baseUrl) for current request. |
115
|
|
|
*/ |
116
|
|
|
protected function getPath(): string |
117
|
|
|
{ |
118
|
|
|
if ($this->request !== null) { |
119
|
|
|
// set base path depending on site config |
120
|
|
|
$site = $this->request->getAttribute('site'); |
121
|
|
|
if ($site !== null && $site instanceof Site) { |
122
|
|
|
$siteBasePath = $this->request->getAttribute('site') |
123
|
|
|
->getBase() |
124
|
|
|
->getPath(); |
125
|
|
|
if ($siteBasePath !== '/' && $siteBasePath[-1] !== '/') { |
126
|
|
|
$siteBasePath .= '/'; |
127
|
|
|
} |
128
|
|
|
} else { |
129
|
|
|
$siteBasePath = '/'; |
130
|
|
|
} |
131
|
|
|
|
132
|
|
|
$this->baseUrl = (string) $this->request->getUri() |
133
|
|
|
->withQuery('') |
134
|
|
|
->withPath($siteBasePath); |
135
|
|
|
|
136
|
|
|
// set url with base path removed |
137
|
|
|
return rtrim( |
138
|
|
|
(string) preg_replace('%^' . preg_quote((string) $siteBasePath, '%') . '%', '', $this->request->getUri()->getPath()), |
139
|
|
|
'/' |
140
|
|
|
); |
141
|
|
|
} |
142
|
|
|
|
143
|
|
|
return parent::getPath(); |
144
|
|
|
} |
145
|
|
|
|
146
|
|
|
/** |
147
|
|
|
* override postCall so that we can cache response via TYPO3-caching-framework - if it's possible |
148
|
|
|
*/ |
149
|
|
|
protected function postCall() |
150
|
|
|
{ |
151
|
|
|
parent::postCall(); |
152
|
|
|
|
153
|
|
|
if ($this->typo3Cache->isResponseCacheableByTypo3Cache($this->requestMethod, $this->apiMethodInfo->metadata)) { |
154
|
|
|
$this->typo3Cache->cacheResponseByTypo3Cache( |
155
|
|
|
$this->responseCode, |
|
|
|
|
156
|
|
|
$this->url, |
157
|
|
|
$_GET, |
158
|
|
|
$this->apiMethodInfo->metadata, |
159
|
|
|
$this->responseData, |
160
|
|
|
$this->responseFormat::class, |
161
|
|
|
headers_list() |
162
|
|
|
); |
163
|
|
|
} |
164
|
|
|
} |
165
|
|
|
|
166
|
|
|
private function handleRequestByTypo3Cache(): string |
167
|
|
|
{ |
168
|
|
|
$cacheEntry = $this->typo3Cache->getCacheEntry($this->url, $_GET); |
169
|
|
|
|
170
|
|
|
if (count($cacheEntry['responseHeaders']) === 0) { |
171
|
|
|
// the cache is from an internal REST-API-call, so we must manually send some headers |
172
|
|
|
@header('Content-Type: application/json; charset=utf-8'); |
|
|
|
|
173
|
|
|
@header('Cache-Control: private, no-cache, no-store, must-revalidate'); |
|
|
|
|
174
|
|
|
} else { |
175
|
|
|
// set/manipulate headers |
176
|
|
|
foreach ($cacheEntry['responseHeaders'] as $responseHeader) { |
177
|
|
|
if (str_starts_with((string) $responseHeader, 'Expires:')) { |
178
|
|
|
if ($cacheEntry['frontendCacheExpires'] === 0) { |
179
|
|
|
$expires = $cacheEntry['frontendCacheExpires']; |
180
|
|
|
} else { |
181
|
|
|
$expires = gmdate('D, d M Y H:i:s \G\M\T', time() + $cacheEntry['frontendCacheExpires']); |
182
|
|
|
} |
183
|
|
|
|
184
|
|
|
@header('Expires: ' . $expires); |
|
|
|
|
185
|
|
|
} else { |
186
|
|
|
@header($responseHeader); |
|
|
|
|
187
|
|
|
} |
188
|
|
|
} |
189
|
|
|
} |
190
|
|
|
|
191
|
|
|
@header('X-Cached-By-Typo3: 1'); |
|
|
|
|
192
|
|
|
|
193
|
|
|
// send data to client |
194
|
|
|
$this->responseCode = $cacheEntry['responseCode']; |
195
|
|
|
$this->responseData = $cacheEntry['responseData']; |
196
|
|
|
return $this->respond(); |
|
|
|
|
197
|
|
|
} |
198
|
|
|
} |
199
|
|
|
|
This check looks for function or method calls that always return null and whose return value is used.
The method
getObject()
can return nothing but null, so it makes no sense to use the return value.The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.