1 | <?php |
||||
2 | |||||
3 | namespace Aoe\Restler\System\Restler; |
||||
4 | |||||
5 | /*************************************************************** |
||||
6 | * Copyright notice |
||||
7 | * |
||||
8 | * (c) 2021 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 | protected ?ServerRequestInterface $request; |
||||
41 | |||||
42 | private Cache $typo3Cache; |
||||
43 | |||||
44 | /***************************************************************************************************************************/ |
||||
45 | /***************************************************************************************************************************/ |
||||
46 | /* Block of methods, which MUST be overriden from parent-class (otherwise we can't use the TYPO3-caching-framework) ********/ |
||||
47 | /***************************************************************************************************************************/ |
||||
48 | /***************************************************************************************************************************/ |
||||
49 | /** |
||||
50 | * Constructor |
||||
51 | * |
||||
52 | * @param bool $productionMode When set to false, it will run in |
||||
53 | * debug mode and parse the class files |
||||
54 | * every time to map it to the URL |
||||
55 | * |
||||
56 | * @param bool $refreshCache will update the cache when set to true |
||||
57 | * @param ServerRequestInterface $request frontend request |
||||
58 | */ |
||||
59 | public function __construct( |
||||
60 | Cache $typo3Cache, |
||||
61 | $productionMode = false, |
||||
62 | $refreshCache = false, |
||||
63 | ServerRequestInterface $request = null |
||||
64 | ) { |
||||
65 | parent::__construct($productionMode, $refreshCache); |
||||
66 | |||||
67 | if (interface_exists(\Psr\Http\Server\MiddlewareInterface::class)) { |
||||
68 | // restler uses echo;die otherwise and then Typo3 standard mechanisms will not be called |
||||
69 | Defaults::$returnResponse = true; |
||||
70 | } |
||||
71 | |||||
72 | // adds format support for application/hal+json |
||||
73 | Scope::$classAliases['HalJsonFormat'] = \Aoe\Restler\System\Restler\Format\HalJsonFormat::class; |
||||
74 | $this->setSupportedFormats('HalJsonFormat'); |
||||
75 | |||||
76 | $this->typo3Cache = $typo3Cache; |
||||
77 | $this->request = $request; |
||||
78 | |||||
79 | // set pathes from request if present |
||||
80 | if ($this->request !== null) { |
||||
81 | $this->url = $this->getPath(); |
||||
82 | } |
||||
83 | } |
||||
84 | |||||
85 | /** |
||||
86 | * Main function for processing the api request |
||||
87 | * and return the response |
||||
88 | */ |
||||
89 | public function handle() |
||||
90 | { |
||||
91 | try { |
||||
92 | // get information about the REST-request (this is required to check, if we can handle the REST-request by TYPO3-cache) |
||||
93 | $this->get(); |
||||
94 | } catch (RestException $restException) { |
||||
95 | // Exception occurred (e.g. 'Error encoding/decoding JSON') during getting information about REST-request: |
||||
96 | // Let restler handle the error (e.g. that JSON could not be read) - and NOT the TYPO3-exception-handling! |
||||
97 | return parent::handle(); |
||||
0 ignored issues
–
show
|
|||||
98 | } |
||||
99 | |||||
100 | if ($this->requestMethod === 'GET' && $this->typo3Cache->hasCacheEntry($this->url, $_GET)) { |
||||
101 | return $this->handleRequestByTypo3Cache(); |
||||
102 | } |
||||
103 | |||||
104 | // if no cache exist: restler should handle the request |
||||
105 | return parent::handle(); |
||||
0 ignored issues
–
show
Are you sure the usage of
parent::handle() targeting Luracast\Restler\Restler::handle() seems to always return null.
This check looks for function or method calls that always return null and whose return value is used. class A
{
function getObject()
{
return null;
}
}
$a = new A();
if ($a->getObject()) {
The method The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.
Loading history...
|
|||||
106 | } |
||||
107 | |||||
108 | /** |
||||
109 | * Rewrap the not accessible private stream in a new one. |
||||
110 | * |
||||
111 | * @return bool|resource |
||||
112 | */ |
||||
113 | public function getRequestStream() |
||||
114 | { |
||||
115 | $stream = fopen('php://temp', 'wb+'); |
||||
116 | fwrite($stream, (string) $this->request->getBody()); |
||||
117 | fseek($stream, 0, SEEK_SET); |
||||
118 | |||||
119 | return $stream; |
||||
120 | } |
||||
121 | |||||
122 | /** |
||||
123 | * Determine path (and baseUrl) for current request. |
||||
124 | */ |
||||
125 | protected function getPath(): string |
||||
126 | { |
||||
127 | if ($this->request !== null) { |
||||
128 | // set base path depending on site config |
||||
129 | $site = $this->request->getAttribute('site'); |
||||
130 | if ($site !== null && $site instanceof Site) { |
||||
131 | $siteBasePath = $this->request->getAttribute('site') |
||||
132 | ->getBase() |
||||
133 | ->getPath(); |
||||
134 | if ($siteBasePath !== '/' && $siteBasePath[-1] !== '/') { |
||||
135 | $siteBasePath .= '/'; |
||||
136 | } |
||||
137 | } else { |
||||
138 | $siteBasePath = '/'; |
||||
139 | } |
||||
140 | |||||
141 | $this->baseUrl = (string) $this->request->getUri() |
||||
142 | ->withQuery('') |
||||
143 | ->withPath($siteBasePath); |
||||
144 | |||||
145 | // set url with base path removed |
||||
146 | return rtrim(preg_replace('%^' . preg_quote($siteBasePath, '%') . '%', '', $this->request->getUri()->getPath()), '/'); |
||||
147 | } |
||||
148 | |||||
149 | return parent::getPath(); |
||||
150 | } |
||||
151 | |||||
152 | /** |
||||
153 | * override postCall so that we can cache response via TYPO3-caching-framework - if it's possible |
||||
154 | */ |
||||
155 | protected function postCall() |
||||
156 | { |
||||
157 | parent::postCall(); |
||||
158 | |||||
159 | if ($this->typo3Cache->isResponseCacheableByTypo3Cache($this->requestMethod, $this->apiMethodInfo->metadata)) { |
||||
160 | $this->typo3Cache->cacheResponseByTypo3Cache( |
||||
161 | $this->responseCode, |
||||
0 ignored issues
–
show
It seems like
$this->responseCode can also be of type null ; however, parameter $responseCode of Aoe\Restler\System\TYPO3...eResponseByTypo3Cache() does only seem to accept integer , maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
Loading history...
|
|||||
162 | $this->url, |
||||
163 | $_GET, |
||||
164 | $this->apiMethodInfo->metadata, |
||||
165 | $this->responseData, |
||||
166 | get_class($this->responseFormat), |
||||
167 | headers_list() |
||||
168 | ); |
||||
169 | } |
||||
170 | } |
||||
171 | |||||
172 | private function handleRequestByTypo3Cache(): string |
||||
173 | { |
||||
174 | $cacheEntry = $this->typo3Cache->getCacheEntry($this->url, $_GET); |
||||
175 | |||||
176 | if (count($cacheEntry['responseHeaders']) === 0) { |
||||
177 | // the cache is from an internal REST-API-call, so we must manually send some headers |
||||
178 | @header('Content-Type: application/json; charset=utf-8'); |
||||
0 ignored issues
–
show
It seems like you do not handle an error condition for
header() . This can introduce security issues, and is generally not recommended.
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
If you suppress an error, we recommend checking for the error condition explicitly: // For example instead of
@mkdir($dir);
// Better use
if (@mkdir($dir) === false) {
throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
Are you sure the usage of
header('Content-Type: ap...n/json; charset=utf-8') is correct as it seems to always return null .
This check looks for function or method calls that always return null and whose return value is used. class A
{
function getObject()
{
return null;
}
}
$a = new A();
if ($a->getObject()) {
The method The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.
Loading history...
|
|||||
179 | @header('Cache-Control: private, no-cache, no-store, must-revalidate'); |
||||
0 ignored issues
–
show
Are you sure the usage of
header('Cache-Control: p...tore, must-revalidate') is correct as it seems to always return null .
This check looks for function or method calls that always return null and whose return value is used. class A
{
function getObject()
{
return null;
}
}
$a = new A();
if ($a->getObject()) {
The method The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.
Loading history...
|
|||||
180 | } else { |
||||
181 | // set/manipulate headers |
||||
182 | foreach ($cacheEntry['responseHeaders'] as $responseHeader) { |
||||
183 | if (substr($responseHeader, 0, 8) === 'Expires:') { |
||||
184 | if ($cacheEntry['frontendCacheExpires'] === 0) { |
||||
185 | $expires = $cacheEntry['frontendCacheExpires']; |
||||
186 | } else { |
||||
187 | $expires = gmdate('D, d M Y H:i:s \G\M\T', time() + $cacheEntry['frontendCacheExpires']); |
||||
188 | } |
||||
189 | |||||
190 | @header('Expires: ' . $expires); |
||||
0 ignored issues
–
show
Are you sure the usage of
header('Expires: ' . $expires) is correct as it seems to always return null .
This check looks for function or method calls that always return null and whose return value is used. class A
{
function getObject()
{
return null;
}
}
$a = new A();
if ($a->getObject()) {
The method The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.
Loading history...
|
|||||
191 | } else { |
||||
192 | @header($responseHeader); |
||||
0 ignored issues
–
show
Are you sure the usage of
header($responseHeader) is correct as it seems to always return null .
This check looks for function or method calls that always return null and whose return value is used. class A
{
function getObject()
{
return null;
}
}
$a = new A();
if ($a->getObject()) {
The method The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.
Loading history...
|
|||||
193 | } |
||||
194 | } |
||||
195 | } |
||||
196 | |||||
197 | @header('X-Cached-By-Typo3: 1'); |
||||
0 ignored issues
–
show
Are you sure the usage of
header('X-Cached-By-Typo3: 1') is correct as it seems to always return null .
This check looks for function or method calls that always return null and whose return value is used. class A
{
function getObject()
{
return null;
}
}
$a = new A();
if ($a->getObject()) {
The method The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.
Loading history...
|
|||||
198 | |||||
199 | // send data to client |
||||
200 | $this->responseCode = $cacheEntry['responseCode']; |
||||
201 | $this->responseData = $cacheEntry['responseData']; |
||||
202 | return $this->respond(); |
||||
0 ignored issues
–
show
|
|||||
203 | } |
||||
204 | } |
||||
205 |
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.