Completed
Push — master ( 71bbf5...711a8e )
by
unknown
17:07
created

StaticRouteResolver::getApplicableStaticRoute()   A

Complexity

Conditions 5
Paths 3

Size

Total Lines 19
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 11
nc 3
nop 3
dl 0
loc 19
rs 9.6111
c 0
b 0
f 0
1
<?php
2
declare(strict_types = 1);
3
4
namespace TYPO3\CMS\Frontend\Middleware;
5
6
/*
7
 * This file is part of the TYPO3 CMS project.
8
 *
9
 * It is free software; you can redistribute it and/or modify it under
10
 * the terms of the GNU General Public License, either version 2
11
 * of the License, or any later version.
12
 *
13
 * For the full copyright and license information, please read the
14
 * LICENSE.txt file that was distributed with this source code.
15
 *
16
 * The TYPO3 project - inspiring people to share!
17
 */
18
19
use Psr\Http\Message\ResponseInterface;
20
use Psr\Http\Message\ServerRequestInterface;
21
use Psr\Http\Server\MiddlewareInterface;
22
use Psr\Http\Server\RequestHandlerInterface;
23
use TYPO3\CMS\Core\Http\HtmlResponse;
24
use TYPO3\CMS\Core\Http\RequestFactory;
25
use TYPO3\CMS\Core\Http\Response;
26
use TYPO3\CMS\Core\LinkHandling\LinkService;
27
use TYPO3\CMS\Core\Resource\File;
28
use TYPO3\CMS\Core\Routing\InvalidRouteArgumentsException;
29
use TYPO3\CMS\Core\Routing\RouterInterface;
30
use TYPO3\CMS\Core\Site\Entity\Site;
31
32
/**
33
 * Resolves static routes - can return configured content directly or load content from file / urls
34
 */
35
class StaticRouteResolver implements MiddlewareInterface
36
{
37
    /**
38
     * @var RequestFactory
39
     */
40
    protected $requestFactory;
41
42
    /**
43
     * @var LinkService
44
     */
45
    protected $linkService;
46
47
    public function __construct(
48
        RequestFactory $requestFactory,
49
        LinkService $linkService
50
    ) {
51
        $this->requestFactory = $requestFactory;
52
        $this->linkService = $linkService;
53
    }
54
55
    /**
56
     * Checks if there is a valid site with route configuration.
57
     *
58
     * @param ServerRequestInterface $request
59
     * @param RequestHandlerInterface $handler
60
     * @return ResponseInterface
61
     */
62
    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
63
    {
64
        if (($site = $request->getAttribute('site', null)) instanceof Site &&
65
            ($configuration = $site->getConfiguration()['routes'] ?? null)
66
        ) {
67
            $path = ltrim($request->getUri()->getPath(), '/');
68
            $routeConfig = $this->getApplicableStaticRoute($configuration, $site, $path);
69
            if (is_array($routeConfig)) {
70
                try {
71
                    [$content, $contentType] = $this->resolveByType($request, $site, $routeConfig['type'], $routeConfig);
72
                } catch (InvalidRouteArgumentsException $e) {
73
                    return new Response('Invalid route', 404, ['Content-Type' => 'text/plain']);
74
                }
75
76
                return new HtmlResponse($content, 200, ['Content-Type' => $contentType]);
77
            }
78
        }
79
        return $handler->handle($request);
80
    }
81
82
    /**
83
     * Find the proper configuration for the static route in the static route configuration. Mainly:
84
     * - needs to have a valid "route" property
85
     * - needs to have a "type"
86
     *
87
     * @param array $staticRouteConfiguration the "routes" part of the site configuration
88
     * @param Site $site the current site where the configuration is based on
89
     * @param string $uriPath the path of the current request - used to match the "route" value of a single static route
90
     * @return array|null the configuration for the static route that matches, or null if no route is given
91
     */
92
    protected function getApplicableStaticRoute(array $staticRouteConfiguration, Site $site, string $uriPath): ?array
93
    {
94
        $routeNames = array_map(function (?string $route) use ($site) {
95
            if ($route === null || $route === '') {
96
                return null;
97
            }
98
            return ltrim(trim($site->getBase()->getPath(), '/') . '/' . ltrim($route, '/'), '/');
99
        }, array_column($staticRouteConfiguration, 'route'));
100
        // Remove empty routes which would throw an error (could happen within creating a false route in the GUI)
101
        $routeNames = array_filter($routeNames);
102
103
        if (in_array($uriPath, $routeNames, true)) {
104
            $key = array_search($uriPath, $routeNames, true);
105
            // Only allow routes with a type "given"
106
            if (isset($staticRouteConfiguration[$key]['type'])) {
107
                return $staticRouteConfiguration[$key];
108
            }
109
        }
110
        return null;
111
    }
112
113
    /**
114
     * @param File $file
115
     * @return array
116
     */
117
    protected function getFromFile(File $file): array
118
    {
119
        $content = $file->getContents();
120
        $contentType = $file->getMimeType();
121
        return [$content, $contentType];
122
    }
123
124
    /**
125
     * @param string $uri
126
     * @return array
127
     */
128
    protected function getFromUri(string $uri): array
129
    {
130
        $response = $this->requestFactory->request($uri);
131
        $contentType = 'text/plain; charset=utf-8';
132
        $content = '';
133
        if ($response->getStatusCode() === 200) {
134
            $content = $response->getBody()->getContents();
135
            $contentType = $response->getHeader('Content-Type');
136
        }
137
138
        return [$content, $contentType];
139
    }
140
141
    /**
142
     * @param ServerRequestInterface $request
143
     * @param Site $site
144
     * @param array $urlParams
145
     * @return string
146
     * @throws InvalidRouteArgumentsException
147
     */
148
    protected function getPageUri(ServerRequestInterface $request, Site $site, array $urlParams): string
149
    {
150
        $parameters = [];
151
        // Add additional parameters, if set via TypoLink
152
        if (isset($urlParams['parameters'])) {
153
            parse_str($urlParams['parameters'], $parameters);
154
        }
155
        $parameters['type'] = $urlParams['pagetype'] ?? 0;
156
        $parameters['_language'] = $request->getAttribute('language', null);
157
        $uri = $site->getRouter()->generateUri(
158
            (int)$urlParams['pageuid'],
159
            $parameters,
160
            '',
161
            RouterInterface::ABSOLUTE_URL
162
        );
163
        return (string)$uri;
164
    }
165
166
    /**
167
     * @param ServerRequestInterface $request
168
     * @param Site $site
169
     * @param string $type
170
     * @param array $routeConfig
171
     * @return array
172
     * @throws InvalidRouteArgumentsException
173
     */
174
    protected function resolveByType(ServerRequestInterface $request, Site $site, string $type, array $routeConfig): array
175
    {
176
        switch ($type) {
177
            case 'staticText':
178
                $content = $routeConfig['content'];
179
                $contentType = 'text/plain; charset=utf-8';
180
                break;
181
            case 'uri':
182
                $urlParams = $this->linkService->resolve($routeConfig['source']);
183
                if ($urlParams['type'] === 'url' || $urlParams['type'] === 'page') {
184
                    $uri = $urlParams['url'] ?? $this->getPageUri($request, $site, $urlParams);
185
                    [$content, $contentType] = $this->getFromUri($uri);
186
                } elseif ($urlParams['type'] === 'file') {
187
                    [$content, $contentType] = $this->getFromFile($urlParams['file']);
188
                } else {
189
                    throw new \InvalidArgumentException('Can only handle URIs of type page, url or file.', 1537348076);
190
                }
191
192
                break;
193
            default:
194
                throw new \InvalidArgumentException(
195
                    'Can only handle static file configurations with type uri or staticText.',
196
                    1537348083
197
                );
198
        }
199
        return [$content, $contentType];
200
    }
201
}
202