Issues (20)

Classes/System/Restler/RestlerExtended.php (10 issues)

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
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 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.

Loading history...
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 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.

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 ignore-type  annotation

161
                /** @scrutinizer ignore-type */ $this->responseCode,
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
Security Best Practice introduced by
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 ignore-unhandled  annotation

178
            /** @scrutinizer ignore-unhandled */ @header('Content-Type: application/json; charset=utf-8');

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 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.

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 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.

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 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.

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 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.

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 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.

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
Bug Best Practice introduced by
The expression return $this->respond() could return the type null which is incompatible with the type-hinted return string. Consider adding an additional type-check to rule them out.
Loading history...
203
    }
204
}
205