MercureAuthorization::__construct()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 10
Code Lines 0

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 0
c 1
b 0
f 0
nc 1
nop 8
dl 0
loc 10
ccs 0
cts 1
cp 0
crap 2
rs 10

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
3
/*
4
 * This file is part of the Silverback API Components Bundle Project
5
 *
6
 * (c) Daniel West <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
declare(strict_types=1);
13
14
namespace Silverback\ApiComponentsBundle\Mercure;
15
16
use ApiPlatform\Exception\OperationNotFoundException as LegacyOperationNotFoundException;
17
use ApiPlatform\Metadata\Exception\OperationNotFoundException;
18
use ApiPlatform\Metadata\HttpOperation;
19
use ApiPlatform\Metadata\Resource\Factory\ResourceMetadataCollectionFactoryInterface;
20
use ApiPlatform\Metadata\Resource\Factory\ResourceNameCollectionFactoryInterface;
21
use Silverback\ApiComponentsBundle\Annotation\Publishable;
22
use Silverback\ApiComponentsBundle\Helper\Publishable\PublishableStatusChecker;
23
use Symfony\Component\HttpFoundation\Cookie;
24
use Symfony\Component\HttpFoundation\RequestStack;
25
use Symfony\Component\Mercure\Authorization;
26
use Symfony\Component\Routing\RequestContext;
27
28
class MercureAuthorization
29
{
30
    public function __construct(
31
        private readonly ResourceNameCollectionFactoryInterface $resourceNameCollectionFactory,
32
        private readonly ResourceMetadataCollectionFactoryInterface $resourceMetadataCollectionFactory,
33
        private readonly PublishableStatusChecker $publishableStatusChecker,
34
        private readonly RequestContext $requestContext,
35
        private readonly Authorization $mercureAuthorization,
36
        private readonly RequestStack $requestStack,
37
        private readonly string $cookieSameSite = Cookie::SAMESITE_STRICT,
38
        private readonly ?string $hubName = null
39
    ) {
40
    }
41
42
    public function getAuthorizationCookie(): Cookie
43
    {
44
        $subscribeTopics = $this->getSubscribeTopics();
45
        $cookie = $this->mercureAuthorization->createCookie($this->requestStack->getCurrentRequest(), $subscribeTopics, null, [], $this->hubName);
0 ignored issues
show
Bug introduced by
It seems like $this->requestStack->getCurrentRequest() can also be of type null; however, parameter $request of Symfony\Component\Mercur...ization::createCookie() does only seem to accept Symfony\Component\HttpFoundation\Request, 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

45
        $cookie = $this->mercureAuthorization->createCookie(/** @scrutinizer ignore-type */ $this->requestStack->getCurrentRequest(), $subscribeTopics, null, [], $this->hubName);
Loading history...
46
47
        return $cookie
48
            ->withSameSite($this->cookieSameSite)
49
            ->withExpires(time() + (10 * 365 * 24 * 60 * 60));
50
    }
51
52
    public function getClearAuthorizationCookie(): Cookie
53
    {
54
        return $this->getAuthorizationCookie();
55
    }
56
57
    public function getSubscribeTopics(): array
58
    {
59
        $subscribeIris = [];
60
        foreach ($this->resourceNameCollectionFactory->create() as $resourceClass) {
61
            if ($resourceIris = $this->getSubscribeIrisForResource($resourceClass)) {
62
                $subscribeIris[] = $resourceIris;
63
            }
64
        }
65
66
        return array_merge([], ...$subscribeIris);
67
    }
68
69
    private function getSubscribeIrisForResource(string $resourceClass): ?array
70
    {
71
        $operation = $this->getMercureResourceOperation($resourceClass);
72
        if (!$operation) {
73
            return null;
74
        }
75
76
        $refl = new \ReflectionClass($operation->getClass());
77
        $isPublishable = \count($refl->getAttributes(Publishable::class));
78
79
        $uriTemplate = $this->buildAbsoluteUriTemplate() . $operation->getRoutePrefix() . $operation->getUriTemplate();
80
        $subscribeIris = [$uriTemplate];
81
82
        if (!$isPublishable) {
83
            return $subscribeIris;
84
        }
85
86
        // Note that `?draft=1` is also hard coded into the PublishableIriConverter, probably make this configurable somewhere
87
        if ($this->publishableStatusChecker->isGranted($operation->getClass())) {
88
            $subscribeIris[] = $uriTemplate . '?draft=1';
89
        }
90
91
        return $subscribeIris;
92
    }
93
94
    private function getMercureResourceOperation(string $resourceClass): ?HttpOperation
95
    {
96
        $resourceMetadataCollection = $this->resourceMetadataCollectionFactory->create($resourceClass);
97
98
        try {
99
            $operation = $resourceMetadataCollection->getOperation(forceCollection: false, httpOperation: true);
100
        } catch (OperationNotFoundException|LegacyOperationNotFoundException) {
101
            return null;
102
        }
103
104
        if (!$operation instanceof HttpOperation) {
105
            return null;
106
        }
107
108
        $mercure = $operation->getMercure();
109
110
        if (!$mercure) {
111
            return null;
112
        }
113
114
        return $operation;
115
    }
116
117
    /**
118
     * Mercure subscribe iris should be absolute
119
     * this code can also be found in Symfony's URL Generator
120
     * but as we work without a symfony route here (and we would not want to do this as it's not spec-compliant)
121
     * we do it by hand.
122
     */
123
    private function buildAbsoluteUriTemplate(): string
124
    {
125
        $scheme = $this->requestContext->getScheme();
126
        $host = $this->requestContext->getHost();
127
        $defaultPort = $this->requestContext->isSecure() ? $this->requestContext->getHttpsPort() : $this->requestContext->getHttpPort();
128
        if (80 !== $defaultPort && 443 !== $defaultPort) {
129
            return sprintf('%s://%s:%d', $scheme, $host, $defaultPort);
130
        }
131
132
        return sprintf('%s://%s', $scheme, $host);
133
    }
134
}
135