Completed
Branch account (35ca3e)
by vincent
02:39
created

Tmdb::getRequest()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 6
ccs 4
cts 4
cp 1
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 4
nc 1
nop 2
crap 1
1
<?php declare(strict_types=1);
2
3
/**
4
 * This file is part of the Tmdb package.
5
 *
6
 * (c) Vincent Faliès <[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
 * @author Vincent Faliès <[email protected]>
12
 * @copyright Copyright (c) 2017
13
 */
14
15
namespace VfacTmdb;
16
17
use VfacTmdb\Interfaces\TmdbInterface;
18
use VfacTmdb\Interfaces\HttpRequestInterface;
19
use Psr\Log\LoggerInterface;
20
use VfacTmdb\Exceptions\TmdbException;
21
use VfacTmdb\Exceptions\IncorrectParamException;
22
use VfacTmdb\Exceptions\ServerErrorException;
23
24
/**
25
 * Tmdb wrapper core class
26
 * @package Tmdb
27
 * @author Vincent Faliès <[email protected]>
28
 * @copyright Copyright (c) 2017
29
 */
30
class Tmdb implements TmdbInterface
31
{
32
33
    /**
34
     * API Key
35
     * @var string
36
     */
37
    private $api_key = null;
38
39
    /**
40
     * Default language for API response
41
     * @var string
42
     */
43
    private $language = 'fr-FR';
44
45
    /**
46
     * Include adult content in search result
47
     * @var bool
48
     */
49
    private $include_adult = false;
50
51
    /**
52
     * API Page result
53
     * @var int
54
     */
55
    private $page = 1;
56
57
    /**
58
     * API configuration
59
     * @var \stdClass
60
     */
61
    protected $configuration = null;
62
63
    /**
64
     * API Genres
65
     * @var \stdClass
66
     */
67
    protected $genres = null;
68
69
    /**
70
     * Base URL of the API
71
     * @var string
72
     */
73
    public $base_api_url = 'https://api.themoviedb.org/';
74
75
    /**
76
     * Logger
77
     * @var LoggerInterface
78
     */
79
    protected $logger = null;
80
81
    /**
82
     * API Version
83
     * @var int
84
     */
85
    protected $version = 3;
86
87
    /**
88
     * Http request object
89
     * @var HttpRequestInterface
90
     */
91
    protected $http_request = null;
92
    /**
93
     * Request object
94
     * @var \stdClass
95
     */
96
    protected $request;
97
    /**
98
     * Last request url
99
     * @var string
100
     */
101
    protected $url = null;
102
103
    /**
104
     * Constructor
105
     * @param string $api_key TMDB API Key
106
     * @param int $version Version of API (Not yet used)
107
     * @param LoggerInterface $logger Logger used in the class
108
     * @param HttpRequestInterface $http_request
109
     */
110 356
    public function __construct(string $api_key, int $version = 3, LoggerInterface $logger, HttpRequestInterface $http_request)
111
    {
112 356
        $this->api_key      = $api_key;
113 356
        $this->logger       = $logger;
114 356
        $this->version      = $version;
115 356
        $this->http_request = $http_request;
116 356
        $this->request      = new \stdClass;
117 356
    }
118
119
    /**
120
     * Send request to TMDB API with GET method
121
     * @param string $action API action to request
122
     * @param array $options Array of options of the request (optional)
123
     * @return \stdClass|null
124
     */
125 317
    public function getRequest(string $action, array $options = array()) : ?\stdClass
126
    {
127 317
        $this->logger->debug('Start sending HTTP request with GET method', array('action' => $action, 'options' => $options));
128 317
        $this->url = $this->buildHTTPUrl($action, $options);
129 317
        return $this->sendRequest('GET', $this->url);
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this->sendRequest('GET', $this->url); (string) is incompatible with the return type declared by the interface VfacTmdb\Interfaces\TmdbInterface::getRequest of type stdClass|null.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
130
    }
131
132
    /**
133
     * Send request to TMDB API with POST method
134
     * @param string $action API action to request
135
     * @param array $options Array of options of the request (optional)
136
     * @param array $form_params form_params for request options
137
     * @return \stdClass|null
138
     */
139 15
    public function postRequest(string $action, array $options = array(), array $form_params = array()) : ?\stdClass
140
    {
141 15
        $this->logger->debug('Start sending HTTP request with POST method', array('action' => $action, 'options' => $options, 'form_params' => $form_params));
142 15
        $this->url = $this->buildHTTPUrl($action, $options);
143 15
        return $this->sendRequest('POST', $this->url, $form_params);
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this->sendReques...is->url, $form_params); (string) is incompatible with the return type declared by the interface VfacTmdb\Interfaces\TmdbInterface::postRequest of type stdClass|null.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
144
    }
145
146
    /**
147
     * Send request to TMDB API with DELETE method
148
     * @param  string $action  API action to request
149
     * @param  array  $options Array of options of the request (optional)
150
     * @return \stdClass|null
151
     */
152 5
    public function deleteRequest(string $action, array $options = array()) : ?\stdClass
153
    {
154 5
        $this->logger->debug('Start sending HTTP request with DELETE method', array('action' => $action, 'options' => $options));
155 5
        $this->url = $this->buildHTTPUrl($action, $options);
156 5
        return $this->sendRequest('DELETE', $this->url);
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this->sendRequest('DELETE', $this->url); (string) is incompatible with the return type declared by the interface VfacTmdb\Interfaces\TmdbInterface::deleteRequest of type stdClass|null.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
157
    }
158
159
    /**
160
     * Send request to TMDB API with GET method
161
     * @param string $method HTTP method (GET, POST)
162
     * @param string $url API url to request
163
     * @param array $form_params form params request options
164
     * @return \stdClass|null
165
     */
166 6
    protected function sendRequest(string $method, string $url, array $form_params = array()) : ?\stdClass
167
    {
168
        try {
169 6
            $res = new \stdClass();
170 6
            $method_name = $method.'Response';
171 6
            $res = $this->http_request->$method_name($url, [], $form_params);
172 6
            $response = $this->decodeRequest($res, $method, $url, $form_params);
173 3
            return $response;
174 3
        } catch (TmdbException $e) {
175 3
            $this->logger->error('sendRequest failed : '.$e->getMessage(), array('method' => $method, 'url' => $url, 'form_params' => $form_params));
176 3
            throw $e;
177
        }
178
    }
179
180
    /**
181
     * Decode request response
182
     * @param  mixed $res
183
     * @param  string $method
184
     * @param  string $url
185
     * @param  array $form_params
186
     * @return string
187
     */
188 6
    private function decodeRequest($res, $method, $url, $form_params)
189
    {
190 6
        if (empty($res->getBody())) {
191 2
            $this->logger->error('Request Body empty', array('method' => $method, 'url' => $url, 'form_params' => $form_params));
192 2
            throw new ServerErrorException();
193
        }
194 4
        $response = json_decode($res->getBody());
195 4
        if (empty($response)) {
196 1
            $this->logger->error('Request Body can not be decode', array('method' => $method, 'url' => $url, 'form_params' => $form_params));
197 1
            throw new ServerErrorException();
198
        }
199 3
        return $response;
200
    }
201
202
    /**
203
     * Build URL for HTTP Call
204
     * @param string $action API action to request
205
     * @param array $options Array of options of the request (optional)
206
     * @return string
207
     */
208 319
    private function buildHTTPUrl(string $action, array $options) : string
209
    {
210
        // Url construction
211 319
        $url = $this->base_api_url . $this->version . '/' . $action;
212
213
        // Parameters
214 319
        $params            = [];
215 319
        $params['api_key'] = $this->api_key;
216
217 319
        $params = array_merge($params, $options);
218
219
        // URL with paramters construction
220 319
        $url = $url . '?' . http_build_query($params);
221
222 319
        return $url;
223
    }
224
225
    /**
226
     * Get API Configuration
227
     * @return \stdClass
228
     * @throws TmdbException
229
     */
230 50
    public function getConfiguration() : \stdClass
231
    {
232
        try {
233 50
            $this->logger->debug('Start getting configuration');
234 50
            if (is_null($this->configuration)) {
235 50
                $this->logger->debug('No configuration found, sending HTTP request to get it');
236 50
                $this->configuration = $this->getRequest('configuration');
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->getRequest('configuration') of type string is incompatible with the declared type object<stdClass> of property $configuration.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
237
            }
238 49
            return $this->configuration;
239 1
        } catch (TmdbException $ex) {
240 1
            throw $ex;
241
        }
242
    }
243
244
    /**
245
     * Get logger
246
     * @return LoggerInterface
247
     */
248 324
    public function getLogger() : LoggerInterface
249
    {
250 324
        return $this->logger;
251
    }
252
253
    /**
254
     * Magical property getter
255
     * @param  string $name Name of the property
256
     * @return string       Value of the property
257
     */
258 73
    public function __get(string $name) : string
259
    {
260
        switch ($name) {
261 73
            case 'url':
262 72
                return $this->$name;
263
            default:
264 1
                throw new IncorrectParamException;
265
        }
266
    }
267
268
    /**
269
     * Check year option and return correct value
270
     * @param array $options
271
     * @param array &$return Return array to save valid option
272
     * @return void
273
     */
274 29
    public function checkOptionYear(array $options, array &$return) : void
275
    {
276 29
        if (isset($options['year'])) {
277 1
            $return['year'] = (int) $options['year'];
278
        }
279 29
    }
280
281
    /**
282
     * Check Language string with format ISO 639-1
283
     * @param array $options
284
     * @param array &$return Return array to save valid option
285
     * @return void
286
     */
287 277
    public function checkOptionLanguage(array $options, array &$return) : void
288
    {
289 277
        if (isset($options['language'])) {
290 35
            $check = preg_match("#([a-z]{2})-([A-Z]{2})#", $options['language']);
291 35
            if ($check === 0 || $check === false) {
292 1
                $this->logger->error('Incorrect language param option', array('language' => $options['language']));
293 1
                throw new IncorrectParamException;
294
            }
295 34
            $return['language'] = $options['language'];
296
        }
297 276
    }
298
299
    /**
300
     * Check include adult option
301
     * @param  array $options
302
     * @param array &$return Return array to save valid option
303
     * @return void
304
     */
305 29
    public function checkOptionIncludeAdult(array $options, array &$return) : void
306
    {
307 29
        if (isset($options['include_adult'])) {
308 1
            $return['include_adult'] = filter_var($options['include_adult'], FILTER_VALIDATE_BOOLEAN);
309
        }
310 29
    }
311
312
    /**
313
     * Check page option
314
     * @param  array  $options
315
     * @param array &$return Return array to save valid option
316
     * @return void
317
     */
318 38
    public function checkOptionPage(array $options, array &$return) : void
319
    {
320 38
        if (isset($options['page'])) {
321 1
            $return['page'] = (int) $options['page'];
322
        }
323 38
    }
324
325
    /**
326
     * Check sort by option
327
     * @param  array  $options
328
     * @param array &$return Return array to save valid option
329
     * @return void
330
     */
331 9
    public function checkOptionSortBy(array $options, array &$return) : void
332
    {
333 9
        if (isset($options['sort_by'])) {
334 2
            switch ($options['sort_by']) {
335 2
                case 'asc':
336 1
                case 'desc':
337 1
                    break;
338
                default:
339 1
                    throw new IncorrectParamException;
340
            }
341 1
            $return['sort_by'] = 'created_at.'.$options['sort_by'];
342
        }
343 8
    }
344
345
    /**
346
     * Check query option
347
     * @param  array  $options
348
     * @param array &$return Return array to save valid option
349
     * @return void
350
     */
351 28
    public function checkOptionQuery(array $options, array &$return) : void
352
    {
353 28
        if (isset($options['query'])) {
354 28
            $return['query'] = trim($options['query']);
355
        }
356 28
    }
357
358
    /**
359
     * Check session_id option
360
     * @param array  $options
361
     * @param array &$return Return array to save valid option
362
     * @return void
363
     */
364 7
    public function checkOptionSessionId(array $options, array &$return) : void
365
    {
366 7
        if (isset($options['session_id'])) {
367 7
            $return['session_id'] = trim($options['session_id']);
368
        }
369 7
    }
370
}
371