Completed
Pull Request — master (#50)
by
unknown
05:51
created

RestlerExtended   A

Complexity

Total Complexity 19

Size/Duplication

Total Lines 165
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 3

Test Coverage

Coverage 0%

Importance

Changes 0
Metric Value
wmc 19
lcom 2
cbo 3
dl 0
loc 165
ccs 0
cts 43
cp 0
rs 10
c 0
b 0
f 0

6 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 19 2
A handle() 0 12 3
B getPath() 0 20 6
A postCall() 0 16 2
A getRequestStream() 0 8 1
A handleRequestByTypo3Cache() 0 30 5
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\TYPO3\Cache as Typo3Cache;
29
use Exception;
30
use Luracast\Restler\Defaults;
31
use Luracast\Restler\RestException;
32
use Luracast\Restler\Restler;
33
use Luracast\Restler\Scope;
34
use Psr\Http\Message\ServerRequestInterface;
35
36
class RestlerExtended extends Restler
37
{
38
    /**
39
     * @var Typo3Cache
40
     */
41
    private $typo3Cache;
42
43
    /** @var ServerRequestInterface  */
44
    protected $request;
45
46
    /***************************************************************************************************************************/
47
    /***************************************************************************************************************************/
48
    /* Block of methods, which MUST be overriden from parent-class (otherwise we can't use the TYPO3-caching-framework) ********/
49
    /***************************************************************************************************************************/
50
    /***************************************************************************************************************************/
51
    /**
52
     * Constructor
53
     *
54
     * @param Typo3Cache $typo3Cache
55
     * @param bool $productionMode    When set to false, it will run in
56
     *                                   debug mode and parse the class files
57
     *                                   every time to map it to the URL
58
     *
59
     * @param bool $refreshCache      will update the cache when set to true
60
     * @param ServerRequestInterface     frontend request
61
     */
62
    public function __construct(Typo3Cache $typo3Cache, $productionMode = false, $refreshCache = false, ServerRequestInterface $request = null)
63
    {
64
        parent::__construct($productionMode, $refreshCache);
65
66
        // restler uses echo;die otherwise and then Typo3 standard mechanisms will not be called
67
        Defaults::$returnResponse = true;
68
69
        // adds format support for application/hal+json
70
        Scope::$classAliases['HalJsonFormat'] = 'Aoe\Restler\System\Restler\Format\HalJsonFormat';
71
        $this->setSupportedFormats('HalJsonFormat');
72
73
        $this->typo3Cache = $typo3Cache;
74
        $this->request = $request;
75
76
        // set pathes from request if present
77
        if ($this->request !== null) {
78
            $this->url = $this->getPath();
79
        }
80
    }
81
82
    /**
83
     * Main function for processing the api request
84
     * and return the response
85
     *
86
     * @throws Exception     when the api service class is missing
87
     * @throws RestException to send error response
88
     */
89
    public function handle()
90
    {
91
        // get information about the REST-request
92
        $this->get();
93
94
        if ($this->requestMethod === 'GET' && $this->typo3Cache->hasCacheEntry($this->url, $_GET)) {
95
            return $this->handleRequestByTypo3Cache();
96
        }
97
98
        // if no cache exist: restler should handle the request
99
        return parent::handle();
100
    }
101
102
    /**
103
     * Determine path (and baseUrl) for current request.
104
     *
105
     * @return string|string[]|null
106
     */
107
    protected function getPath()
108
    {
109
        if ($this->request !== null) {
110
            // set base path depending on site config
111
            $site = $this->request->getAttribute('site');
112
            if ($site !== null && $site instanceof \TYPO3\CMS\Core\Site\Entity\Site) {
113
                $siteBasePath = $this->request->getAttribute('site')->getBase()->getPath();
114
                if ($siteBasePath !== '/' && $siteBasePath[-1] !== '/') {
115
                    $siteBasePath .= '/';
116
                }
117
            } else {
118
                $siteBasePath = '/';
119
            }
120
            $this->baseUrl = (string)$this->request->getUri()->withQuery('')->withPath($siteBasePath);
121
122
            // set url with base path removed
123
            return rtrim(preg_replace('%^' . preg_quote($siteBasePath, '%') . '%', '', $this->request->getUri()->getPath()), '/');
124
        }
125
        return parent::getPath();
126
    }
127
128
    /**
129
     * override postCall so that we can cache response via TYPO3-caching-framework - if it's possible
130
     */
131
    protected function postCall()
132
    {
133
        parent::postCall();
134
135
        if ($this->typo3Cache->isResponseCacheableByTypo3Cache($this->requestMethod, $this->apiMethodInfo->metadata)) {
136
            $this->typo3Cache->cacheResponseByTypo3Cache(
137
                $this->responseCode,
138
                $this->url,
139
                $_GET,
140
                $this->apiMethodInfo->metadata,
141
                $this->responseData,
142
                get_class($this->responseFormat),
143
                headers_list()
144
            );
145
        }
146
    }
147
148
    /**
149
     * Rewrap the not accessible private stream in a new one.
150
     *
151
     * @return bool|resource
152
     */
153
    public function getRequestStream()
154
    {
155
        $stream = fopen('php://temp', 'wb+');
156
        fwrite($stream, (string)$this->request->getBody());
157
        fseek($stream, 0, SEEK_SET);
158
159
        return $stream;
160
    }
161
162
    /***************************************************************************************************************************/
163
    /***************************************************************************************************************************/
164
    /* Block of methods, which does NOT override logic from parent-class *******************************************************/
165
    /***************************************************************************************************************************/
166
    /***************************************************************************************************************************/
167
    /**
168
     * @return string
169
     */
170
    private function handleRequestByTypo3Cache()
171
    {
172
        $cacheEntry = $this->typo3Cache->getCacheEntry($this->url, $_GET);
173
174
        if (count($cacheEntry['responseHeaders']) === 0) {
175
            // the cache is from an internal REST-API-call, so we must manually send some headers
176
            @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 here. This can introduce security issues, and is generally not recommended.

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...
177
            @header('Cache-Control: private, no-cache, no-store, must-revalidate');
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

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...
178
        } else {
179
            // set/manipulate headers
180
            foreach ($cacheEntry['responseHeaders'] as $responseHeader) {
181
                if (substr($responseHeader, 0, 8) === 'Expires:') {
182
                    if ($cacheEntry['frontendCacheExpires'] === 0) {
183
                        $expires = $cacheEntry['frontendCacheExpires'];
184
                    } else {
185
                        $expires = gmdate('D, d M Y H:i:s \G\M\T', time() + $cacheEntry['frontendCacheExpires']);
186
                    }
187
                    @header('Expires: ' . $expires);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

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...
188
                } else {
189
                    @header($responseHeader);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

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...
190
                }
191
            }
192
        }
193
        @header('X-Cached-By-Typo3: 1');
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

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...
194
195
        // send data to client
196
        $this->responseCode = $cacheEntry['responseCode'];
197
        $this->responseData = $cacheEntry['responseData'];
198
        return $this->respond();
199
    }
200
}
201