Failed Conditions
Pull Request — task/3376-TYPO3_12_compatibili... (#3584)
by Benni
36:30
created

SolrRoutingMiddleware::process()   B

Complexity

Conditions 9
Paths 9

Size

Total Lines 87
Code Lines 43

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 21
CRAP Score 24.8039

Importance

Changes 4
Bugs 0 Features 0
Metric Value
eloc 43
dl 0
loc 87
ccs 21
cts 50
cp 0.42
rs 7.6764
c 4
b 0
f 0
cc 9
nc 9
nop 2
crap 24.8039

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of the TYPO3 CMS project.
7
 *
8
 * It is free software; you can redistribute it and/or modify it under
9
 * the terms of the GNU General Public License, either version 2
10
 * of the License, or any later version.
11
 *
12
 * For the full copyright and license information, please read the
13
 * LICENSE.txt file that was distributed with this source code.
14
 *
15
 * The TYPO3 project - inspiring people to share!
16
 */
17
18
namespace ApacheSolrForTypo3\Solr\Middleware;
19
20
use ApacheSolrForTypo3\Solr\IndexQueue\PageIndexerRequest;
21
use ApacheSolrForTypo3\Solr\Routing\RoutingService;
22
use Psr\Http\Message\ResponseInterface;
23
use Psr\Http\Message\ServerRequestInterface;
24
use Psr\Http\Message\UriInterface;
25
use Psr\Http\Server\MiddlewareInterface;
26
use Psr\Http\Server\RequestHandlerInterface;
27
use Psr\Log\LoggerAwareInterface;
28
use Psr\Log\LoggerAwareTrait;
29
use TYPO3\CMS\Core\Routing\SiteRouteResult;
30
use TYPO3\CMS\Core\Site\Entity\Site;
31
use TYPO3\CMS\Core\Site\Entity\SiteLanguage;
32
use TYPO3\CMS\Core\Utility\GeneralUtility;
0 ignored issues
show
Bug introduced by
The type TYPO3\CMS\Core\Utility\GeneralUtility was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
33
34
/**
35
 * Middleware to create beautiful URLs for Solr
36
 *
37
 * How to use:
38
 * Inside your extension create following file
39
 * Configuration/RequestMiddlewares.php
40
 *
41
 * return [
42
 *   'frontend' => [
43
 *     'apache-solr-for-typo3/solr-route-enhancer' => [
44
 *       'target' => \ApacheSolrForTypo3\Solr\Middleware\SolrRoutingMiddleware::class,
45
 *       'before' => [
46
 *         'typo3/cms-frontend/site',
47
 *       ]
48
 *     ]
49
 *   ]
50
 * ];
51
 *
52
 * @author Lars Tode <[email protected]>
53
 * @see https://docs.typo3.org/m/typo3/reference-coreapi/main/en-us/ApiOverview/RequestHandling/Index.html
54
 */
55
class SolrRoutingMiddleware implements MiddlewareInterface, LoggerAwareInterface
56
{
57
    use LoggerAwareTrait;
58
59
    /**
60
     * Solr parameter key
61
     */
62
    protected string $namespace = 'tx_solr';
63
64
    /**
65
     * Settings from enhancer configuration
66
     */
67
    protected array $settings = [];
68
69
    protected ?SiteLanguage $language;
70
71
    protected ?RoutingService $routingService = null;
72
73
    /**
74
     * Inject the routing service.
75
     * Used in unit tests too
76
     */
77
    public function injectRoutingService(RoutingService $routingService): void
78
    {
79
        $this->routingService = $routingService;
80
    }
81
82
    /**
83
     * Process the request
84
     */
85 51
    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
86
    {
87 51
        if (!$request->hasHeader(PageIndexerRequest::SOLR_INDEX_HEADER)) {
88 38
            return $handler->handle($request);
89
        }
90
91
        /* @var SiteRouteResult $routeResult */
92 44
        $routeResult = $this->getRoutingService()
93 44
            ->getSiteMatcher()
94 44
            ->matchRequest($request);
95
96 44
        $site = $routeResult->getSite();
97
98 44
        if (!$site instanceof Site) {
99
            return $handler->handle($request);
100
        }
101
102 44
        $this->language = $routeResult->getLanguage();
103
104 44
        if (!($this->language instanceof SiteLanguage)) {
105
            return $handler->handle($request);
106
        }
107
108 44
        $page = $this->retrievePageInformation(
109 44
            $request->getUri(),
110 44
            $site
111 44
        );
112
113 44
        if (empty($page['uid'])) {
114
            return $handler->handle($request);
115
        }
116
117 44
        $enhancerConfiguration = $this->getEnhancerConfiguration(
118 44
            $site,
119 44
            $this->language->getLanguageId() === 0 ? (int)$page['uid'] : (int)$page['l10n_parent']
120 44
        );
121
122 44
        if ($enhancerConfiguration === null) {
123 44
            return $handler->handle($request);
124
        }
125
126
        $this->configure($enhancerConfiguration);
127
128
        /*
129
         * Take slug path segments and argument from incoming URI
130
         */
131
        [$slug, $parameters] = $this->extractParametersFromUriPath(
132
            $request->getUri(),
133
            $enhancerConfiguration['routePath'],
134
            $page['slug'] ?? ''
135
        );
136
137
        /*
138
         * Convert path arguments to query arguments
139
         */
140
        if (!empty($parameters)) {
141
            $request = $this->getRoutingService()->addPathArgumentsToQuery(
142
                $request,
143
                $enhancerConfiguration['_arguments'],
144
                $parameters
145
            );
146
        }
147
148
        /*
149
         * Replace internal URI with existing site taken from path information
150
         * We removed a possible path segment from the slug, that again needs to attach.
151
         *
152
         * NOTE: TypoScript is not available at this point!
153
         */
154
        $uri = $request->getUri()->withPath(
155
            $this->getRoutingService()->cleanupHeadingSlash(
156
                $this->language->getBase()->getPath() .
157
                $page['slug'] ?? ''
158
            )
159
        );
160
        $request = $request->withUri($uri);
161
        $queryParams = $request->getQueryParams();
0 ignored issues
show
Bug introduced by
The method getQueryParams() does not exist on Psr\Http\Message\RequestInterface. It seems like you code against a sub-type of Psr\Http\Message\RequestInterface such as Psr\Http\Message\ServerRequestInterface or GuzzleHttp\Psr7\ServerRequest or TYPO3\CMS\Core\Http\ServerRequest. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

161
        /** @scrutinizer ignore-call */ 
162
        $queryParams = $request->getQueryParams();
Loading history...
162
        $queryParams = $this->getRoutingService()->unmaskQueryParameters($queryParams);
163
        $queryParams = $this->getRoutingService()->inflateQueryParameter($queryParams);
164
165
        // @todo Drop cHash, but need to recalculate
166
        if (array_key_exists('cHash', $queryParams)) {
167
            unset($queryParams['cHash']);
168
        }
169
170
        $request = $request->withQueryParams($queryParams);
0 ignored issues
show
Bug introduced by
The method withQueryParams() does not exist on Psr\Http\Message\RequestInterface. It seems like you code against a sub-type of Psr\Http\Message\RequestInterface such as Psr\Http\Message\ServerRequestInterface or GuzzleHttp\Psr7\ServerRequest or TYPO3\CMS\Core\Http\ServerRequest. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

170
        /** @scrutinizer ignore-call */ 
171
        $request = $request->withQueryParams($queryParams);
Loading history...
171
        return $handler->handle($request);
172
    }
173
174
    /**
175
     * Configures the middleware by enhancer configuration
176
     */
177
    protected function configure(array $enhancerConfiguration): void
178
    {
179
        $this->settings = $enhancerConfiguration['solr'];
180
        $this->namespace = $enhancerConfiguration['extensionKey'] ?? $this->namespace;
181
        $this->routingService = null;
182
    }
183
184
    /**
185
     * Retrieve the enhancer configuration for given site
186
     */
187 43
    protected function getEnhancerConfiguration(Site $site, int $pageUid): ?array
188
    {
189 43
        $enhancers = $this->getRoutingService()->fetchEnhancerInSiteConfigurationByPageUid(
190 43
            $site,
191 43
            $pageUid
192 43
        );
193
194 43
        if (empty($enhancers)) {
195 43
            return null;
196
        }
197
198
        return $enhancers[0];
199
    }
200
201
    /**
202
     * Extract the slug and all arguments from path
203
     */
204
    protected function extractParametersFromUriPath(UriInterface $uri, string $path, string $pageSlug): array
205
    {
206
        // URI get path returns the path with given language parameter
207
        // The parameter pageSlug itself does not contain the language parameter.
208
        $uriPath = $this->getRoutingService()->stripLanguagePrefixFromPath(
209
            $this->language,
0 ignored issues
show
Bug introduced by
It seems like $this->language can also be of type null; however, parameter $language of ApacheSolrForTypo3\Solr\...anguagePrefixFromPath() does only seem to accept TYPO3\CMS\Core\Site\Entity\SiteLanguage, 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

209
            /** @scrutinizer ignore-type */ $this->language,
Loading history...
210
            $uri->getPath()
211
        );
212
213
        if ($uriPath === $pageSlug) {
214
            return [
215
                $pageSlug,
216
                [],
217
            ];
218
        }
219
220
        // Remove slug from URI path in order to ensure only the arguments left
221
        if (mb_substr($uriPath, 0, mb_strlen($pageSlug) + 1) === $pageSlug . '/') {
222
            $length = mb_strlen($pageSlug) + 1;
223
            $uriPath = mb_substr($uriPath, $length, mb_strlen($uriPath) - $length);
224
        }
225
226
        // Take care the format of configuration and given slug equals
227
        $uriPath = $this->getRoutingService()->removeHeadingSlash($uriPath);
228
        $path = $this->getRoutingService()->removeHeadingSlash($path);
229
230
        // Remove begin
231
        $uriElements = explode('/', $uriPath);
232
        $routeElements = explode('/', $path);
233
        $slugElements = [];
234
        $arguments = [];
235
        $process = true;
236
        /*
237
         * Extract the slug elements, until the amount of route elements reached
238
         */
239
        do {
240
            if (count($uriElements) > count($routeElements)) {
241
                $slugElements[] = array_shift($uriElements);
242
            } else {
243
                $process = false;
244
            }
245
        } while ($process);
246
247
        if (empty($routeElements[0])) {
248
            array_shift($routeElements);
249
        }
250
        if (empty($uriElements[0])) {
251
            array_shift($uriElements);
252
        }
253
254
        // Extract the values
255
        $uriElementsCount = count($uriElements);
256
        for ($i = 0; $i < $uriElementsCount; $i++) {
257
            // Skip empty elements
258
            if (empty($uriElements[$i])) {
259
                continue;
260
            }
261
262
            $key = substr($routeElements[$i], 1, strlen($routeElements[$i]) - 1);
263
            $key = substr($key, 0, strlen($key) - 1);
264
265
            $arguments[$key] = $uriElements[$i];
266
        }
267
268
        return [
269
            implode('/', $slugElements),
270
            $arguments,
271
        ];
272
    }
273
274
    /**
275
     * Retrieve the page uid to filter the route enhancer
276
     */
277 43
    protected function retrievePageInformation(UriInterface $uri, Site $site): array
278
    {
279 43
        $path = $this->getRoutingService()->stripLanguagePrefixFromPath(
280 43
            $this->language,
0 ignored issues
show
Bug introduced by
It seems like $this->language can also be of type null; however, parameter $language of ApacheSolrForTypo3\Solr\...anguagePrefixFromPath() does only seem to accept TYPO3\CMS\Core\Site\Entity\SiteLanguage, 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

280
            /** @scrutinizer ignore-type */ $this->language,
Loading history...
281 43
            $uri->getPath()
282 43
        );
283 43
        $slugProvider = $this->getRoutingService()->getSlugCandidateProvider($site);
284 43
        $scan = true;
285 43
        $page = [];
286
        do {
287 43
            $items = $slugProvider->getCandidatesForPath(
288 43
                $path,
289 43
                $this->language
0 ignored issues
show
Bug introduced by
It seems like $this->language can also be of type null; however, parameter $language of TYPO3\CMS\Core\Routing\P...:getCandidatesForPath() does only seem to accept TYPO3\CMS\Core\Site\Entity\SiteLanguage, 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

289
                /** @scrutinizer ignore-type */ $this->language
Loading history...
290 43
            );
291 43
            if (empty($items)) {
292
                $this->logger
293
                    ->/** @scrutinizer ignore-call */
294
                    error(
295
                        vsprintf(
296
                            'Could not determine page for slug "%1$s" and language "%2$s". Given path "%3$s"',
297
                            [
298
                                $path,
299
                                $this->language->getLocale()->getLanguageCode(),
0 ignored issues
show
Bug introduced by
The method getLocale() does not exist on null. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

299
                                $this->language->/** @scrutinizer ignore-call */ 
300
                                                 getLocale()->getLanguageCode(),

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
300
                                $uri->getPath(),
301
                            ]
302
                        )
303
                    );
304
                $scan = false;
305 43
            } elseif (empty($path)) {
306
                $this->logger
307
                    ->/** @scrutinizer ignore-call */
308
                    error(
309
                        vsprintf(
310
                            'Could not resolve page by path "%1$s" and language "%2$s".',
311
                            [
312
                                $uri->getPath(),
313
                                $this->language->getLocale()->getLanguageCode(),
314
                            ]
315
                        )
316
                    );
317
                $scan = false;
318
            } else {
319 43
                foreach ($items as $item) {
320 43
                    $this->logger
321 43
                        ->info(
0 ignored issues
show
Bug introduced by
The method info() does not exist on null. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

321
                        ->/** @scrutinizer ignore-call */ 
322
                          info(

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
322 43
                            vsprintf(
323 43
                                'Path "%1$s" -> slug "%2$s"',
324 43
                                [
325 43
                                    $path,
326 43
                                    $item['slug'],
327 43
                                ]
328 43
                            )
329 43
                        );
330
331 43
                    if ($item['slug'] === $path) {
332 43
                        $page = $item;
333 43
                        $scan = false;
334 43
                        break;
335
                    }
336
                }
337
338 43
                if ($scan) {
339
                    $elements = explode('/', $path);
340
                    if (empty($elements)) {
341
                        $scan = false;
342
                    } else {
343
                        array_pop($elements);
344
                        $path = implode('/', $elements);
345
                    }
346
                }
347
            }
348 43
        } while ($scan);
349 43
        return $page;
350
    }
351
352 43
    protected function getRoutingService(): RoutingService
353
    {
354 43
        if ($this->routingService === null) {
355 43
            $this->routingService = GeneralUtility::makeInstance(
356 43
                RoutingService::class,
357 43
                $this->settings,
358 43
                $this->namespace
359 43
            );
360
        } else {
361 43
            $this->routingService = $this->routingService->withSettings($this->settings);
362
        }
363 43
        return $this->routingService;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->routingService could return the type null which is incompatible with the type-hinted return ApacheSolrForTypo3\Solr\Routing\RoutingService. Consider adding an additional type-check to rule them out.
Loading history...
364
    }
365
}
366