Completed
Push — master ( 038254...115ca0 )
by Alejandro
06:27
created

NotFoundHandler::handle()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 23
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 14
CRAP Score 4

Importance

Changes 0
Metric Value
cc 4
eloc 14
nc 4
nop 1
dl 0
loc 23
ccs 14
cts 14
cp 1
crap 4
rs 9.7998
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Shlinkio\Shlink\Core\Response;
6
7
use Fig\Http\Message\StatusCodeInterface;
8
use InvalidArgumentException;
9
use Psr\Http\Message\ResponseInterface;
10
use Psr\Http\Message\ServerRequestInterface;
11
use Psr\Http\Message\UriInterface;
12
use Psr\Http\Server\RequestHandlerInterface;
13
use Shlinkio\Shlink\Core\Action\RedirectAction;
14
use Shlinkio\Shlink\Core\Options\NotFoundRedirectOptions;
15
use Zend\Diactoros\Response;
16
use Zend\Expressive\Router\RouteResult;
17
use Zend\Expressive\Template\TemplateRendererInterface;
18
19
use function array_shift;
20
use function explode;
21
use function Functional\contains;
22
use function rtrim;
23
24
class NotFoundHandler implements RequestHandlerInterface
25
{
26
    public const NOT_FOUND_TEMPLATE = 'ShlinkCore::error/404';
27
    public const INVALID_SHORT_CODE_TEMPLATE = 'ShlinkCore::invalid-short-code';
28
29
    /** @var TemplateRendererInterface */
30
    private $renderer;
31
    /** @var NotFoundRedirectOptions */
32
    private $redirectOptions;
33
    /** @var string */
34
    private $shlinkBasePath;
35
36 10
    public function __construct(
37
        TemplateRendererInterface $renderer,
38
        NotFoundRedirectOptions $redirectOptions,
39
        string $shlinkBasePath
40
    ) {
41 10
        $this->renderer = $renderer;
42 10
        $this->redirectOptions = $redirectOptions;
43 10
        $this->shlinkBasePath = $shlinkBasePath;
44
    }
45
46
    /**
47
     * Dispatch the next available middleware and return the response.
48
     *
49
     * @param ServerRequestInterface $request
50
     *
51
     * @return ResponseInterface
52
     * @throws InvalidArgumentException
53
     */
54 10
    public function handle(ServerRequestInterface $request): ResponseInterface
55
    {
56
        /** @var RouteResult $routeResult */
57 10
        $routeResult = $request->getAttribute(RouteResult::class, RouteResult::fromRouteFailure(null));
58 10
        $redirectResponse = $this->createRedirectResponse($routeResult, $request->getUri());
59 10
        if ($redirectResponse !== null) {
60 4
            return $redirectResponse;
61
        }
62
63 6
        $accepts = explode(',', $request->getHeaderLine('Accept'));
64 6
        $accept = array_shift($accepts);
65 6
        $status = StatusCodeInterface::STATUS_NOT_FOUND;
66
67
        // If the first accepted type is json, return a json response
68 6
        if (contains(['application/json', 'text/json', 'application/x-json'], $accept)) {
69 3
            return new Response\JsonResponse([
70 3
                'error' => 'NOT_FOUND',
71
                'message' => 'Not found',
72 3
            ], $status);
73
        }
74
75 3
        $template = $routeResult->isFailure() ? self::NOT_FOUND_TEMPLATE : self::INVALID_SHORT_CODE_TEMPLATE;
76 3
        return new Response\HtmlResponse($this->renderer->render($template), $status);
77
    }
78
79 10
    private function createRedirectResponse(RouteResult $routeResult, UriInterface $uri): ?ResponseInterface
80
    {
81 10
        $isBaseUrl = rtrim($uri->getPath(), '/') === $this->shlinkBasePath;
82
83 10
        if ($isBaseUrl && $this->redirectOptions->hasBaseUrlRedirect()) {
84 2
            return new Response\RedirectResponse($this->redirectOptions->getBaseUrlRedirect());
85
        }
86
87 8
        if (!$isBaseUrl && $routeResult->isFailure() && $this->redirectOptions->hasRegular404Redirect()) {
88 1
            return new Response\RedirectResponse($this->redirectOptions->getRegular404Redirect());
89
        }
90
91
        if (
92 7
            $routeResult->isSuccess() &&
93 7
            $routeResult->getMatchedRouteName() === RedirectAction::class &&
94 7
            $this->redirectOptions->hasInvalidShortUrlRedirect()
95
        ) {
96 1
            return new Response\RedirectResponse($this->redirectOptions->getInvalidShortUrlRedirect());
97
        }
98
99 6
        return null;
100
    }
101
}
102