Passed
Push — main ( c81305...16cc9f )
by Daniel
15:49 queued 17s
created

MercureAuthorization::__construct()   A

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
namespace Silverback\ApiComponentsBundle\Mercure;
4
5
use ApiPlatform\Exception\OperationNotFoundException;
6
use ApiPlatform\Metadata\HttpOperation;
7
use ApiPlatform\Metadata\Resource\Factory\ResourceMetadataCollectionFactoryInterface;
8
use ApiPlatform\Metadata\Resource\Factory\ResourceNameCollectionFactoryInterface;
9
use Silverback\ApiComponentsBundle\Annotation\Publishable;
10
use Silverback\ApiComponentsBundle\Helper\Publishable\PublishableStatusChecker;
11
use Symfony\Component\HttpFoundation\Cookie;
12
use Symfony\Component\HttpFoundation\RequestStack;
13
use Symfony\Component\Mercure\Authorization;
14
use Symfony\Component\Routing\RequestContext;
15
16
class MercureAuthorization
17
{
18
    public function __construct(
19
        private readonly ResourceNameCollectionFactoryInterface $resourceNameCollectionFactory,
20
        private readonly ResourceMetadataCollectionFactoryInterface $resourceMetadataCollectionFactory,
21
        private readonly PublishableStatusChecker $publishableStatusChecker,
22
        private readonly RequestContext $requestContext,
23
        private readonly Authorization $mercureAuthorization,
24
        private readonly RequestStack $requestStack,
25
        private readonly string $cookieSameSite = Cookie::SAMESITE_STRICT,
26
        private readonly ?string $hubName = null
27
    ) {
28
    }
29
30
    public function createAuthorizationCookie(): Cookie
31
    {
32
        $subscribeTopics = $this->getSubscribeTopics();
33
        // Todo: await merge of https://github.com/symfony/mercure/pull/93 to remove ability to publish any updates and set to  null
34
        // May also be able to await a mercure bundle update to set the cookie samesite in mercure configs
35
        $cookie = $this->mercureAuthorization->createCookie($this->requestStack->getCurrentRequest(), $subscribeTopics, [], [], $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

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