Completed
Pull Request — master (#50)
by
unknown
06:13
created

RestlerExtended::getPath()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 16

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 0
Metric Value
dl 0
loc 16
ccs 0
cts 3
cp 0
rs 9.7333
c 0
b 0
f 0
cc 3
nc 3
nop 0
crap 12
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
            $siteBasePath = $this->request->getAttribute('site')->getBase()->getPath();
118
            if ($siteBasePath !== '/') {
119
                $siteBasePath .= '/';
120
            }
121
            $this->baseUrl = (string)$this->request->getUri()->withQuery('')->withPath($siteBasePath);
122
123
            // set url with base path removed
124
            return preg_replace('%^' . preg_quote($siteBasePath, '%') . '%', '', $this->request->getUri()->getPath());
125
        } else {
126
            return parent::getPath();
127
        }
128
    }
129
130
    /**
131
     * override postCall so that we can cache response via TYPO3-caching-framework - if it's possible
132
     */
133
    protected function postCall()
134
    {
135
        parent::postCall();
136
137
        if ($this->typo3Cache->isResponseCacheableByTypo3Cache($this->requestMethod, $this->apiMethodInfo->metadata)) {
138
            $this->typo3Cache->cacheResponseByTypo3Cache(
139
                $this->responseCode,
140
                $this->url,
141
                $_GET,
142
                $this->apiMethodInfo->metadata,
143
                $this->responseData,
144
                get_class($this->responseFormat),
145
                headers_list()
146
            );
147
        }
148
    }
149
150
    /**
151
     * Rewrap the not accessible private stream in a new one.
152
     *
153
     * @return bool|resource
154
     */
155
    public function getRequestStream()
156
    {
157
        $stream = fopen('php://temp', 'wb+');
158
        fwrite($stream, (string)$this->request->getBody());
159
        fseek($stream, 0, SEEK_SET);
160
161
        return $stream;
162
    }
163
164
    /***************************************************************************************************************************/
165
    /***************************************************************************************************************************/
166
    /* Block of methods, which does NOT override logic from parent-class *******************************************************/
167
    /***************************************************************************************************************************/
168
    /***************************************************************************************************************************/
169
    /**
170
     * @return string
171
     */
172
    private function handleRequestByTypo3Cache()
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 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...
179
            @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...
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
                    @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...
190
                } else {
191
                    @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...
192
                }
193
            }
194
        }
195
        @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...
196
197
        // send data to client
198
        $this->responseCode = $cacheEntry['responseCode'];
199
        $this->responseData = $cacheEntry['responseData'];
200
        return $this->respond();
201
    }
202
}
203