1
|
|
|
<?php |
2
|
|
|
namespace Aoe\Restler\System\Restler; |
3
|
|
|
|
4
|
|
|
/*************************************************************** |
5
|
|
|
* Copyright notice |
6
|
|
|
* |
7
|
|
|
* (c) 2016 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\Restler\Format\HalJsonFormat; |
29
|
|
|
use Aoe\Restler\System\TYPO3\Cache as Typo3Cache; |
30
|
|
|
use Luracast\Restler\Defaults; |
31
|
|
|
use Luracast\Restler\Restler; |
32
|
|
|
use Luracast\Restler\RestException; |
33
|
|
|
use Exception; |
34
|
|
|
use Luracast\Restler\Scope; |
35
|
|
|
use Psr\Http\Message\ServerRequestInterface; |
36
|
|
|
use Symfony\Component\DependencyInjection\Tests\Compiler\H; |
37
|
|
|
|
38
|
|
|
/** |
39
|
|
|
* @package Restler |
40
|
|
|
*/ |
41
|
|
|
class RestlerExtended extends Restler |
42
|
|
|
{ |
43
|
|
|
/** |
44
|
|
|
* @var Typo3Cache |
45
|
|
|
*/ |
46
|
|
|
private $typo3Cache; |
47
|
|
|
|
48
|
|
|
|
49
|
|
|
/** @var ServerRequestInterface */ |
50
|
|
|
protected $request; |
51
|
|
|
|
52
|
|
|
/***************************************************************************************************************************/ |
53
|
|
|
/***************************************************************************************************************************/ |
54
|
|
|
/* Block of methods, which MUST be overriden from parent-class (otherwise we can't use the TYPO3-caching-framework) ********/ |
55
|
|
|
/***************************************************************************************************************************/ |
56
|
|
|
/***************************************************************************************************************************/ |
57
|
|
|
/** |
58
|
|
|
* Constructor |
59
|
|
|
* |
60
|
|
|
* @param Typo3Cache $typo3Cache |
61
|
|
|
* @param boolean $productionMode When set to false, it will run in |
62
|
|
|
* debug mode and parse the class files |
63
|
|
|
* every time to map it to the URL |
64
|
|
|
* |
65
|
|
|
* @param boolean $refreshCache will update the cache when set to true |
66
|
|
|
* @param ServerRequestInterface frontend request |
67
|
|
|
*/ |
68
|
|
|
public function __construct(Typo3Cache $typo3Cache, $productionMode = false, $refreshCache = false, ServerRequestInterface $request = null) |
69
|
|
|
{ |
70
|
|
|
parent::__construct($productionMode, $refreshCache); |
71
|
|
|
|
72
|
|
|
// restler uses echo;die otherwise and then Typo3 standard mechanisms will not be called |
73
|
|
|
Defaults::$returnResponse = true; |
74
|
|
|
|
75
|
|
|
// adds format support for application/hal+json |
76
|
|
|
Scope::$classAliases['HalJsonFormat'] = 'Aoe\Restler\System\Restler\Format\HalJsonFormat'; |
77
|
|
|
$this->setSupportedFormats('HalJsonFormat'); |
78
|
|
|
|
79
|
|
|
$this->typo3Cache = $typo3Cache; |
80
|
|
|
$this->request = $request; |
81
|
|
|
|
82
|
|
|
// set pathes from request if present |
83
|
|
|
if ($this->request !== null) { |
84
|
|
|
$this->url = $this->getPath(); |
85
|
|
|
} |
86
|
|
|
} |
87
|
|
|
|
88
|
|
|
/** |
89
|
|
|
* Main function for processing the api request |
90
|
|
|
* and return the response |
91
|
|
|
* |
92
|
|
|
* @throws Exception when the api service class is missing |
93
|
|
|
* @throws RestException to send error response |
94
|
|
|
*/ |
95
|
|
|
public function handle() |
96
|
|
|
{ |
97
|
|
|
// get information about the REST-request |
98
|
|
|
$this->get(); |
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(); |
106
|
|
|
} |
107
|
|
|
|
108
|
|
|
/** |
109
|
|
|
* Determine path (and baseUrl) for current request. |
110
|
|
|
* |
111
|
|
|
* @return string|string[]|null |
112
|
|
|
*/ |
113
|
|
|
protected function getPath() |
114
|
|
|
{ |
115
|
|
|
if ($this->request !== null) { |
116
|
|
|
// set base path depending on site config |
117
|
|
|
$site = $this->request->getAttribute('site'); |
118
|
|
|
if ($site !== null && $site instanceof TYPO3\CMS\Core\Site\Entity\Site) { |
|
|
|
|
119
|
|
|
$siteBasePath = $this->request->getAttribute('site')->getBase()->getPath(); |
120
|
|
|
if ($siteBasePath !== '/') { |
121
|
|
|
$siteBasePath .= '/'; |
122
|
|
|
} |
123
|
|
|
} else { |
124
|
|
|
$siteBasePath = '/'; |
125
|
|
|
} |
126
|
|
|
$this->baseUrl = (string)$this->request->getUri()->withQuery('')->withPath($siteBasePath); |
127
|
|
|
|
128
|
|
|
// set url with base path removed |
129
|
|
|
return preg_replace('%^' . preg_quote($siteBasePath, '%') . '%', '', $this->request->getUri()->getPath()); |
130
|
|
|
} else { |
131
|
|
|
return parent::getPath(); |
132
|
|
|
} |
133
|
|
|
} |
134
|
|
|
|
135
|
|
|
/** |
136
|
|
|
* override postCall so that we can cache response via TYPO3-caching-framework - if it's possible |
137
|
|
|
*/ |
138
|
|
|
protected function postCall() |
139
|
|
|
{ |
140
|
|
|
parent::postCall(); |
141
|
|
|
|
142
|
|
|
if ($this->typo3Cache->isResponseCacheableByTypo3Cache($this->requestMethod, $this->apiMethodInfo->metadata)) { |
143
|
|
|
$this->typo3Cache->cacheResponseByTypo3Cache( |
144
|
|
|
$this->responseCode, |
145
|
|
|
$this->url, |
146
|
|
|
$_GET, |
147
|
|
|
$this->apiMethodInfo->metadata, |
148
|
|
|
$this->responseData, |
149
|
|
|
get_class($this->responseFormat), |
150
|
|
|
headers_list() |
151
|
|
|
); |
152
|
|
|
} |
153
|
|
|
} |
154
|
|
|
|
155
|
|
|
/** |
156
|
|
|
* Rewrap the not accessible private stream in a new one. |
157
|
|
|
* |
158
|
|
|
* @return bool|resource |
159
|
|
|
*/ |
160
|
|
|
public function getRequestStream() |
161
|
|
|
{ |
162
|
|
|
$stream = fopen('php://temp', 'wb+'); |
163
|
|
|
fwrite($stream, (string)$this->request->getBody()); |
164
|
|
|
fseek($stream, 0, SEEK_SET); |
165
|
|
|
|
166
|
|
|
return $stream; |
167
|
|
|
} |
168
|
|
|
|
169
|
|
|
/***************************************************************************************************************************/ |
170
|
|
|
/***************************************************************************************************************************/ |
171
|
|
|
/* Block of methods, which does NOT override logic from parent-class *******************************************************/ |
172
|
|
|
/***************************************************************************************************************************/ |
173
|
|
|
/***************************************************************************************************************************/ |
174
|
|
|
/** |
175
|
|
|
* @return string |
176
|
|
|
*/ |
177
|
|
|
private function handleRequestByTypo3Cache() |
178
|
|
|
{ |
179
|
|
|
$cacheEntry = $this->typo3Cache->getCacheEntry($this->url, $_GET); |
180
|
|
|
|
181
|
|
|
if (count($cacheEntry['responseHeaders']) === 0) { |
182
|
|
|
// the cache is from an internal REST-API-call, so we must manually send some headers |
183
|
|
|
@header('Content-Type: application/json; charset=utf-8'); |
|
|
|
|
184
|
|
|
@header('Cache-Control: private, no-cache, no-store, must-revalidate'); |
|
|
|
|
185
|
|
|
} else { |
186
|
|
|
// set/manipulate headers |
187
|
|
|
foreach ($cacheEntry['responseHeaders'] as $responseHeader) { |
188
|
|
|
if (substr($responseHeader, 0, 8) === 'Expires:') { |
189
|
|
|
if ($cacheEntry['frontendCacheExpires'] === 0) { |
190
|
|
|
$expires = $cacheEntry['frontendCacheExpires']; |
191
|
|
|
} else { |
192
|
|
|
$expires = gmdate('D, d M Y H:i:s \G\M\T', time() + $cacheEntry['frontendCacheExpires']); |
193
|
|
|
} |
194
|
|
|
@header('Expires: '.$expires); |
|
|
|
|
195
|
|
|
} else { |
196
|
|
|
@header($responseHeader); |
|
|
|
|
197
|
|
|
} |
198
|
|
|
} |
199
|
|
|
} |
200
|
|
|
@header('X-Cached-By-Typo3: 1'); |
|
|
|
|
201
|
|
|
|
202
|
|
|
// send data to client |
203
|
|
|
$this->responseCode = $cacheEntry['responseCode']; |
204
|
|
|
$this->responseData = $cacheEntry['responseData']; |
205
|
|
|
return $this->respond(); |
206
|
|
|
} |
207
|
|
|
} |
208
|
|
|
|
This error could be the result of:
1. Missing dependencies
PHP Analyzer uses your
composer.json
file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects thecomposer.json
to be in the root folder of your repository.Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the
require
orrequire-dev
section?2. Missing use statement
PHP does not complain about undefined classes in
ìnstanceof
checks. For example, the following PHP code will work perfectly fine:If you have not tested against this specific condition, such errors might go unnoticed.