Passed
Push — master ( 4b2c1a...a53d55 )
by Maximilian
02:53
created

Request::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 9
Code Lines 0

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 1
CRAP Score 1

Importance

Changes 0
Metric Value
eloc 0
dl 0
loc 9
ccs 1
cts 1
cp 1
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 7
crap 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace MaxBeckers\AmazonAlexa\Request;
6
7
use MaxBeckers\AmazonAlexa\Exception\MissingRequestDataException;
8
use MaxBeckers\AmazonAlexa\Exception\MissingRequiredHeaderException;
9
use MaxBeckers\AmazonAlexa\Helper\PropertyHelper;
10
use MaxBeckers\AmazonAlexa\Request\Request\AbstractRequest;
11
use MaxBeckers\AmazonAlexa\Request\Request\AlexaSkillEvent\SkillAccountLinkedRequest;
12
use MaxBeckers\AmazonAlexa\Request\Request\AlexaSkillEvent\SkillDisabledRequest;
13
use MaxBeckers\AmazonAlexa\Request\Request\AlexaSkillEvent\SkillEnabledRequest;
14
use MaxBeckers\AmazonAlexa\Request\Request\AlexaSkillEvent\SkillPermissionAcceptedRequest;
15
use MaxBeckers\AmazonAlexa\Request\Request\AlexaSkillEvent\SkillPermissionChangedRequest;
16
use MaxBeckers\AmazonAlexa\Request\Request\AudioPlayer\PlaybackFailedRequest;
17
use MaxBeckers\AmazonAlexa\Request\Request\AudioPlayer\PlaybackFinishedRequest;
18
use MaxBeckers\AmazonAlexa\Request\Request\AudioPlayer\PlaybackNearlyFinishedRequest;
19
use MaxBeckers\AmazonAlexa\Request\Request\AudioPlayer\PlaybackStartedRequest;
20
use MaxBeckers\AmazonAlexa\Request\Request\AudioPlayer\PlaybackStoppedRequest;
21
use MaxBeckers\AmazonAlexa\Request\Request\CanFulfill\CanFulfillIntentRequest;
22
use MaxBeckers\AmazonAlexa\Request\Request\Display\ElementSelectedRequest;
23
use MaxBeckers\AmazonAlexa\Request\Request\GameEngine\InputHandlerEvent;
24
use MaxBeckers\AmazonAlexa\Request\Request\PlaybackController\NextCommandIssued;
25
use MaxBeckers\AmazonAlexa\Request\Request\PlaybackController\PauseCommandIssued;
26
use MaxBeckers\AmazonAlexa\Request\Request\PlaybackController\PlayCommandIssued;
27
use MaxBeckers\AmazonAlexa\Request\Request\PlaybackController\PreviousCommandIssued;
28
use MaxBeckers\AmazonAlexa\Request\Request\Standard\IntentRequest;
29
use MaxBeckers\AmazonAlexa\Request\Request\Standard\LaunchRequest;
30
use MaxBeckers\AmazonAlexa\Request\Request\Standard\SessionEndedRequest;
31
use MaxBeckers\AmazonAlexa\Request\Request\System\ConnectionsResponseRequest;
32
use MaxBeckers\AmazonAlexa\Request\Request\System\ExceptionEncounteredRequest;
33
34
class Request
35
{
36
    /**
37
     * List of all supported amazon request types.
38
     */
39
    public const REQUEST_TYPES = [
40
        // Standard types
41
        IntentRequest::TYPE => IntentRequest::class,
42
        LaunchRequest::TYPE => LaunchRequest::class,
43
        SessionEndedRequest::TYPE => SessionEndedRequest::class,
44
        // AudioPlayer types
45
        PlaybackStartedRequest::TYPE => PlaybackStartedRequest::class,
46
        PlaybackNearlyFinishedRequest::TYPE => PlaybackNearlyFinishedRequest::class,
47
        PlaybackFinishedRequest::TYPE => PlaybackFinishedRequest::class,
48
        PlaybackStoppedRequest::TYPE => PlaybackStoppedRequest::class,
49
        PlaybackFailedRequest::TYPE => PlaybackFailedRequest::class,
50
        // PlaybackController types
51
        NextCommandIssued::TYPE => NextCommandIssued::class,
52
        PauseCommandIssued::TYPE => PauseCommandIssued::class,
53
        PlayCommandIssued::TYPE => PlayCommandIssued::class,
54
        PreviousCommandIssued::TYPE => PreviousCommandIssued::class,
55
        // System types
56
        ExceptionEncounteredRequest::TYPE => ExceptionEncounteredRequest::class,
57
        // Display types
58
        ElementSelectedRequest::TYPE => ElementSelectedRequest::class,
59
        // Game engine types
60
        InputHandlerEvent::TYPE => InputHandlerEvent::class,
61
        // can fulfill intent
62
        CanFulfillIntentRequest::TYPE => CanFulfillIntentRequest::class,
63
        // Connections Response Request
64
        ConnectionsResponseRequest::TYPE => ConnectionsResponseRequest::class,
65
        // Skill event types
66
        SkillAccountLinkedRequest::TYPE => SkillAccountLinkedRequest::class,
67
        SkillEnabledRequest::TYPE => SkillEnabledRequest::class,
68
        SkillDisabledRequest::TYPE => SkillDisabledRequest::class,
69
        SkillPermissionAcceptedRequest::TYPE => SkillPermissionAcceptedRequest::class,
70
        SkillPermissionChangedRequest::TYPE => SkillPermissionChangedRequest::class,
71
    ];
72
73 58
    public function __construct(
74
        public ?string $version = null,
75
        public ?Session $session = null,
76
        public ?Context $context = null,
77
        public ?AbstractRequest $request = null,
78
        public string $amazonRequestBody = '',
79
        public string $signatureCertChainUrl = '',
80
        public string $signature = '',
81
    ) {
82 58
    }
83
84
    /**
85
     * @throws MissingRequiredHeaderException
86
     * @throws MissingRequestDataException
87
     */
88 44
    public static function fromAmazonRequest(string $amazonRequestBody, string $signatureCertChainUrl, string $signature): self
89
    {
90 44
        $request = new self();
91
92 44
        $request->signatureCertChainUrl = $signatureCertChainUrl;
93 44
        $request->signature = $signature;
94 44
        $request->amazonRequestBody = $amazonRequestBody;
95 44
        $amazonRequest = (array) json_decode($amazonRequestBody, true);
96
97 44
        $request->version = PropertyHelper::checkNullValueString($amazonRequest, 'version');
98 44
        $request->session = isset($amazonRequest['session']) ? Session::fromAmazonRequest($amazonRequest['session']) : null;
99 44
        $request->context = isset($amazonRequest['context']) ? Context::fromAmazonRequest($amazonRequest['context']) : null;
100
101 44
        $request->setRequest($amazonRequest);
102 43
        $request->checkSignature();
103
104 42
        return $request;
105
    }
106
107 3
    public function getApplicationId(): ?string
108
    {
109
        // workaround for developer console
110 3
        if ($this->session && $this->session->application) {
111 3
            return $this->session->application->applicationId;
112
        } elseif ($this->context && ($system = $this->context->system) && $system->application) {
113
            return $system->application->applicationId;
114
        }
115
116
        return null;
117
    }
118
119
    /**
120
     * @throws MissingRequestDataException
121
     */
122 44
    private function setRequest(array $amazonRequest): void
123
    {
124 44
        if (!isset($amazonRequest['request']['type']) || !isset(self::REQUEST_TYPES[$amazonRequest['request']['type']])) {
125 1
            throw new MissingRequestDataException();
126
        }
127 43
        $this->request = (self::REQUEST_TYPES[$amazonRequest['request']['type']])::fromAmazonRequest($amazonRequest['request']);
128
    }
129
130
    /**
131
     * @throws MissingRequiredHeaderException
132
     */
133 43
    private function checkSignature(): void
134
    {
135 43
        if ($this->request->validateSignature() && (!$this->signatureCertChainUrl || !$this->signature)) {
0 ignored issues
show
Bug introduced by
The method validateSignature() 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

135
        if ($this->request->/** @scrutinizer ignore-call */ validateSignature() && (!$this->signatureCertChainUrl || !$this->signature)) {

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...
136 1
            throw new MissingRequiredHeaderException();
137
        }
138
    }
139
}
140