Passed
Push — master ( 5addfc...e8c424 )
by Manuel
59s queued 11s
created

Request::doExecute()   B

Complexity

Conditions 6
Paths 7

Size

Total Lines 49
Code Lines 25

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 6
eloc 25
c 1
b 0
f 0
nc 7
nop 0
dl 0
loc 49
rs 8.8977
1
<?php declare(strict_types=1);
2
3
namespace Ticketpark\SaferpayJson\Request;
4
5
use Doctrine\Common\Annotations\AnnotationRegistry;
6
use GuzzleHttp\Exception\ClientException;
7
use GuzzleHttp\Psr7\Response as GuzzleResponse;
8
use JMS\Serializer\Annotation\Exclude;
9
use JMS\Serializer\Annotation\SerializedName;
10
use JMS\Serializer\Annotation\VirtualProperty;
11
use JMS\Serializer\SerializerBuilder;
12
use JMS\Serializer\SerializerInterface;
13
use Ticketpark\SaferpayJson\Request\Exception\HttpRequestException;
14
use Ticketpark\SaferpayJson\Request\Container\RequestHeader;
15
use Ticketpark\SaferpayJson\Request\Exception\SaferpayErrorException;
16
use Ticketpark\SaferpayJson\Response\ErrorResponse;
17
use Ticketpark\SaferpayJson\Response\Response;
18
19
abstract class Request
20
{
21
    private const ROOT_URL = 'https://www.saferpay.com/api/';
22
    private const ROOT_URL_TEST = 'https://test.saferpay.com/api/';
23
    private const ERROR_RESPONSE_CLASS = ErrorResponse::class;
24
25
    /**
26
     * @var RequestConfig
27
     * @Exclude
28
     */
29
    private $requestConfig;
30
31
    /**
32
     * We want the implementation to define the exact return type hint of the response.
33
     * In PHP 7.4 the return type hint here in the abstraction would therefore be Ticketpark\SaferpayJson\Response\Response,
34
     * as all other responses inherit from that class.
35
     * In PHP 7.3 this is not yet allowed. Therefore the return type is omitted and only provided as a PhpDoc in
36
     * order to satisfy static code analysis by PhpStan.
37
     *
38
     * @link https://wiki.php.net/rfc/covariant-returns-and-contravariant-parameters
39
     * @link https://stitcher.io/blog/new-in-php-74#improved-type-variance-rfc
40
     * @return mixed
41
     */
42
    abstract public function execute();
43
44
    abstract public function getApiPath(): string;
45
    abstract public function getResponseClass(): string;
46
47
    public function __construct(RequestConfig $requestConfig)
48
    {
49
        $this->requestConfig = $requestConfig;
50
    }
51
52
    /**
53
     * @SerializedName("RequestHeader")
54
     * @VirtualProperty
55
     */
56
    public function getRequestHeader(): RequestHeader
57
    {
58
        return new RequestHeader(
59
            $this->requestConfig->getCustomerId()
60
        );
61
    }
62
63
    public function getRequestConfig(): RequestConfig
64
    {
65
        return $this->requestConfig;
66
    }
67
68
    protected function doExecute(): Response
69
    {
70
        try {
71
            /** @var GuzzleResponse $response */
72
            $response = $this->requestConfig->getClient()->post(
73
                $this->getUrl(),
74
                [
75
                    'headers' => $this->getHeaders(),
76
                    'body' => $this->getContent()
77
                ]
78
            );
79
        } catch (\Exception $e) {
80
            if (!$e instanceof ClientException) {
81
                throw new HttpRequestException($e->getMessage());
82
            }
83
84
            /** @var GuzzleResponse $response */
85
            $response = $e->getResponse();
86
        }
87
88
        $statusCode = $response->getStatusCode();
89
90
        if ($statusCode >= 400 && $statusCode < 500) {
91
92
            /** @var ErrorResponse $errorResponse */
93
            $errorResponse = $this->getSerializer()->deserialize(
94
                (string) $response->getBody(),
95
                self::ERROR_RESPONSE_CLASS,
96
                'json'
97
            );
98
99
            throw new SaferpayErrorException($errorResponse);
100
        }
101
102
        if (200 !== $statusCode) {
103
            throw new HttpRequestException(sprintf(
104
                'Unexpected http request response with status code %s.',
105
                $response->getStatusCode()
106
            ));
107
        }
108
109
        /** @var Response $libraryResponse */
110
        $libraryResponse = $this->getSerializer()->deserialize(
111
            (string) $response->getBody(),
112
            $this->getResponseClass(),
113
            'json'
114
        );
115
116
        return $libraryResponse;
117
    }
118
119
    private function getUrl(): string
120
    {
121
        $rootUrl = self::ROOT_URL;
122
123
        if ($this->requestConfig->isTest()) {
124
            $rootUrl = self::ROOT_URL_TEST;
125
        }
126
127
        return $rootUrl . $this->getApiPath();
128
    }
129
130
    private function getHeaders(): array
131
    {
132
        return [
133
            'Content-Type'  => 'application/json; charset=utf-8',
134
            'Accept'        => 'application/json',
135
            'Authorization' => 'Basic ' . base64_encode(
136
                $this->requestConfig->getApiKey()
137
                . ':'
138
                . $this->requestConfig->getApiSecret()
139
            )
140
        ];
141
    }
142
143
    private function getContent(): string
144
    {
145
        return $this->getSerializer()->serialize($this, 'json');
146
    }
147
148
    private function getSerializer(): SerializerInterface
149
    {
150
        AnnotationRegistry::registerLoader('class_exists');
0 ignored issues
show
Deprecated Code introduced by
The function Doctrine\Common\Annotati...istry::registerLoader() has been deprecated: This method is deprecated and will be removed in doctrine/annotations 2.0. Annotations will be autoloaded in 2.0. ( Ignorable by Annotation )

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

150
        /** @scrutinizer ignore-deprecated */ AnnotationRegistry::registerLoader('class_exists');

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
151
152
        return SerializerBuilder::create()->build();
153
    }
154
}
155