ApiAbstract::initPropertiesFromEnvironment()   A
last analyzed

Complexity

Conditions 4
Paths 4

Size

Total Lines 16
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 8
nc 4
nop 0
dl 0
loc 16
rs 9.2
c 0
b 0
f 0
1
<?php
2
3
namespace GinoPane\PHPolyglot\API\Implementation;
4
5
use GinoPane\NanoRest\NanoRest;
6
use GinoPane\NanoRest\Request\RequestContext;
7
use GinoPane\NanoRest\Exceptions\TransportException;
8
use GinoPane\NanoRest\Response\ResponseContextAbstract;
9
use GinoPane\NanoRest\Exceptions\ResponseContextException;
10
use GinoPane\PHPolyglot\API\Response\ApiResponseInterface;
11
use GinoPane\PHPolyglot\Exception\InvalidPropertyException;
12
use GinoPane\PHPolyglot\Exception\BadResponseContextException;
13
use GinoPane\PHPolyglot\Exception\InvalidEnvironmentException;
14
use GinoPane\PHPolyglot\Exception\MethodDoesNotExistException;
15
16
/**
17
 * Class ApiAbstract
18
 *
19
 * @author Sergey <Gino Pane> Karavay
20
 */
21
abstract class ApiAbstract
22
{
23
    /**
24
     * Instance of HTTP client to handle requests
25
     *
26
     * @var NanoRest
27
     */
28
    protected $httpClient;
29
30
    /**
31
     * Main API endpoint
32
     *
33
     * @var string
34
     */
35
    protected $apiEndpoint = '';
36
37
    /**
38
     * Mapping of properties to environment variables which must supply these properties, like this:
39
     *
40
     *      [
41
     *          'apiKey' => 'ENVIRONMENT_API_KEY'
42
     *      ]
43
     *
44
     * These properties and corresponding environment variables will be validated
45
     *
46
     * @var array
47
     */
48
    protected $envProperties = [];
49
50
    /**
51
     * ApiAbstract constructor
52
     *
53
     * @param array $parameters
54
     *
55
     * @throws InvalidEnvironmentException
56
     * @throws InvalidPropertyException
57
     */
58
    public function __construct(array $parameters = [])
59
    {
60
        $this->httpClient = new NanoRest();
61
62
        $this->initPropertiesFromEnvironment();
63
    }
64
65
    /**
66
     * Call API method by creating RequestContext, sending it, filtering the result and preparing the response
67
     *
68
     * @param string $apiClassMethod
69
     * @param array  $arguments Arguments that need to be passed to API-related methods
70
     *
71
     * @throws TransportException
72
     * @throws ResponseContextException
73
     * @throws BadResponseContextException
74
     * @throws MethodDoesNotExistException
75
     *
76
     * @return ApiResponseInterface
77
     */
78
    protected function callApi(string $apiClassMethod, array $arguments = []): ApiResponseInterface
79
    {
80
        $requestContext = $this->getApiRequestContext($apiClassMethod, $arguments);
81
82
        $responseContext = $this->getApiResponseContext($requestContext);
83
84
        $apiResponse = $this->prepareApiResponse($responseContext, $apiClassMethod);
85
86
        return $apiResponse;
87
    }
88
89
    /**
90
     * Filters ResponseContext from common HTTP errors
91
     *
92
     * @param ResponseContextAbstract $responseContext
93
     *
94
     * @throws BadResponseContextException
95
     *
96
     * @return void
97
     */
98
    protected function processApiResponseContextErrors(ResponseContextAbstract $responseContext): void
99
    {
100
        if ($responseContext->hasHttpError()) {
101
            throw new BadResponseContextException(
102
                $responseContext->getHttpStatusMessage(),
103
                $responseContext->getHttpStatusCode()
104
            );
105
        }
106
    }
107
108
    /**
109
     * Fills specified properties using environment variables
110
     *
111
     * @throws InvalidPropertyException
112
     * @throws InvalidEnvironmentException
113
     */
114
    protected function initPropertiesFromEnvironment(): void
115
    {
116
        foreach ($this->envProperties as $property => $env) {
117
            if (!property_exists($this, $property)) {
118
                throw new InvalidPropertyException(
119
                    sprintf("Property \"%s\" does not exist within the class \"%s\"", $property, get_class($this))
120
                );
121
            }
122
123
            if (false === ($envSetting = getenv($env))) {
124
                throw new InvalidEnvironmentException(
125
                    sprintf("Required environment variable \"%s\" is not set", $env)
126
                );
127
            }
128
129
            $this->{$property} = $envSetting;
130
        }
131
    }
132
133
    /**
134
     * Accepts RequestContext and sends it to API to get ResponseContext
135
     *
136
     * @param RequestContext $requestContext
137
     *
138
     * @throws TransportException
139
     * @throws ResponseContextException
140
     * @throws BadResponseContextException
141
     *
142
     * @return ResponseContextAbstract
143
     */
144
    private function getApiResponseContext(RequestContext $requestContext): ResponseContextAbstract
145
    {
146
        $responseContext = $this->httpClient->sendRequest(
147
            $requestContext
148
        );
149
150
        $this->processApiResponseContextErrors($responseContext);
151
152
        return $responseContext;
153
    }
154
155
    /**
156
     * Gets RequestContext for sending
157
     *
158
     * @param string $apiClassMethod
159
     * @param array $arguments Arguments that need to be passed to API-related methods
160
     *
161
     * @return RequestContext
162
     *
163
     * @throws MethodDoesNotExistException
164
     */
165
    private function getApiRequestContext(string $apiClassMethod, array $arguments = []): RequestContext
166
    {
167
        $getRequestContext = sprintf("create%sContext", ucfirst($apiClassMethod));
168
169
        $this->assertMethodExists($getRequestContext);
170
171
        $requestContext = $this->{$getRequestContext}(...$arguments);
172
173
        return $requestContext;
174
    }
175
176
    /**
177
     * Prepares API response by processing ResponseContext
178
     *
179
     * @param ResponseContextAbstract $responseContext
180
     * @param string $apiClassMethod
181
     *
182
     * @throws MethodDoesNotExistException
183
     *
184
     * @return ApiResponseInterface
185
     */
186
    private function prepareApiResponse(
187
        ResponseContextAbstract $responseContext,
188
        string $apiClassMethod
189
    ): ApiResponseInterface {
190
        $prepareApiResponse = sprintf("prepare%sResponse", ucfirst($apiClassMethod));
191
192
        $this->assertMethodExists($prepareApiResponse);
193
194
        $apiResponse = $this->{$prepareApiResponse}($responseContext);
195
196
        return $apiResponse;
197
    }
198
199
    /**
200
     * Throws exception if method does not exist
201
     *
202
     * @param string $method
203
     *
204
     * @throws MethodDoesNotExistException
205
     *
206
     * @return void
207
     */
208
    private function assertMethodExists(string $method): void
209
    {
210
        if (!method_exists($this, $method)) {
211
            throw new MethodDoesNotExistException(
212
                sprintf("Specified method \"%s\" does not exist", $method)
213
            );
214
        }
215
    }
216
}
217