Completed
Branch develop (205409)
by Timothy
07:06
created

KitsuTrait::setRequestBuilder()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 3
nc 1
nop 1
dl 0
loc 5
rs 9.4285
c 0
b 0
f 0
1
<?php declare(strict_types=1);
2
/**
3
 * Anime List Client
4
 *
5
 * An API client for Kitsu and MyAnimeList to manage anime and manga watch lists
6
 *
7
 * PHP version 7
8
 *
9
 * @package     AnimeListClient
10
 * @author      Timothy J. Warren <[email protected]>
11
 * @copyright   2015 - 2017  Timothy J. Warren
12
 * @license     http://www.opensource.org/licenses/mit-license.html  MIT License
13
 * @version     4.0
14
 * @link        https://github.com/timw4mail/HummingBirdAnimeClient
15
 */
16
17
namespace Aviat\AnimeClient\API\Kitsu;
18
19
use Aviat\AnimeClient\AnimeClient;
20
use Aviat\AnimeClient\API\GuzzleTrait;
21
use Aviat\AnimeClient\API\Kitsu as K;
22
use Aviat\Ion\Json;
23
use GuzzleHttp\Client;
24
use GuzzleHttp\Cookie\CookieJar;
25
use GuzzleHttp\Psr7\Response;
26
use InvalidArgumentException;
27
use RuntimeException;
28
29
trait KitsuTrait {
30
	
31
	/**
32
	 * The request builder for the MAL API
33
	 * @var MALRequestBuilder
34
	 */
35
	protected $requestBuilder;
36
	
37
	/**
38
     * The Guzzle http client object
39
     * @var object
40
     */
41
    protected $client;
42
43
    /**
44
     * Cookie jar object for api requests
45
     * @var object
46
     */
47
    protected $cookieJar;
48
49
	/**
50
	 * The base url for api requests
51
	 * @var string $base_url
52
	 */
53
	protected $baseUrl = "https://kitsu.io/api/edge/";
54
55
	/**
56
	 * HTTP headers to send with every request
57
	 *
58
	 * @var array
59
	 */
60
	protected $defaultHeaders = [
61
		'User-Agent' => "Tim's Anime Client/4.0",
62
		'Accept-Encoding' => 'application/vnd.api+json',
63
		'Content-Type' => 'application/vnd.api+json',
64
		'client_id' => 'dd031b32d2f56c990b1425efe6c42ad847e7fe3ab46bf1299f05ecd856bdb7dd',
65
		'client_secret' => '54d7307928f63414defd96399fc31ba847961ceaecef3a5fd93144e960c0e151',
66
	];
67
	
68
	/**
69
	 * Set the request builder object
70
	 *
71
	 * @param KitsuRequestBuilder $requestBuilder
72
	 * @return self
73
	 */
74
	public function setRequestBuilder($requestBuilder): self
75
	{
76
		$this->requestBuilder = $requestBuilder;
0 ignored issues
show
Documentation Bug introduced by
It seems like $requestBuilder of type object<Aviat\AnimeClient...su\KitsuRequestBuilder> is incompatible with the declared type object<Aviat\AnimeClient...itsu\MALRequestBuilder> of property $requestBuilder.

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...
77
		return $this;
78
	}
79
80
	/**
81
	 * Set up the class properties
82
	 *
83
	 * @return void
84
	 */
85
	protected function init()
86
	{
87
		$defaults = [
88
			'cookies' => $this->cookieJar,
89
			'headers' => $this->defaultHeaders,
90
			'timeout' => 25,
91
			'connect_timeout' => 25
92
		];
93
94
		$this->cookieJar = new CookieJar();
95
		$this->client = new Client([
96
			'base_uri' => $this->baseUrl,
97
			'cookies' => TRUE,
98
			'http_errors' => TRUE,
99
			'defaults' => $defaults
100
		]);
101
	}
102
103
	/**
104
	 * Make a request via Guzzle
105
	 *
106
	 * @param string $type
107
	 * @param string $url
108
	 * @param array $options
109
	 * @return Response
110
	 */
111
	private function getResponse(string $type, string $url, array $options = [])
112
	{
113
		$logger = null;
0 ignored issues
show
Unused Code introduced by
$logger is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
114
		$validTypes = ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'];
115
116
		if ( ! in_array($type, $validTypes))
117
		{
118
			throw new InvalidArgumentException('Invalid http request type');
119
		}
120
121
		$defaultOptions = [
122
			'headers' => $this->defaultHeaders
123
		];
124
125
		$logger = $this->container->getLogger('kitsu-request');
0 ignored issues
show
Bug introduced by
The property container does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
126
		$sessionSegment = $this->getContainer()
0 ignored issues
show
Bug introduced by
It seems like getContainer() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
127
			->get('session')
128
			->getSegment(AnimeClient::SESSION_SEGMENT);
129
130
		if ($sessionSegment->get('auth_token') !== null && $url !== K::AUTH_URL)
131
		{
132
			$token = $sessionSegment->get('auth_token');
133
			$defaultOptions['headers']['Authorization'] = "bearer {$token}";
134
		}
135
136
		$options = array_merge($defaultOptions, $options);
137
138
		$response = $this->client->request($type, $url, $options);
139
140
		$logger->debug('Kitsu API request', [
141
			'requestParams' => [
142
				'type' => $type,
143
				'url' => $url,
144
			],
145
			'responseValues' => [
146
				'status' => $response->getStatusCode()
147
			]
148
		]);
149
150
		return $response;
151
	}
152
153
	/**
154
	 * Make a request via Guzzle
155
	 *
156
	 * @param string $type
157
	 * @param string $url
158
	 * @param array $options
159
	 * @return array
160
	 */
161 View Code Duplication
	private function request(string $type, string $url, array $options = []): array
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
162
	{
163
		$logger = null;
164
		if ($this->getContainer())
0 ignored issues
show
Bug introduced by
It seems like getContainer() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
165
		{
166
			$logger = $this->container->getLogger('kitsu-request');
167
		}
168
169
		$response = $this->getResponse($type, $url, $options);
170
171
		if ((int) $response->getStatusCode() > 299 || (int) $response->getStatusCode() < 200)
172
		{
173
			if ($logger)
174
			{
175
				$logger->warning('Non 200 response for api call', $response->getBody());
176
			}
177
		}
178
179
		return JSON::decode($response->getBody(), TRUE);
180
	}
181
182
	/**
183
	 * Remove some boilerplate for get requests
184
	 *
185
	 * @param array $args
186
	 * @return array
187
	 */
188
	protected function getRequest(...$args): array
189
	{
190
		return $this->request('GET', ...$args);
0 ignored issues
show
Documentation introduced by
$args is of type array<integer,array>, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
191
	}
192
193
	/**
194
	 * Remove some boilerplate for patch requests
195
	 *
196
	 * @param array $args
197
	 * @return array
198
	 */
199
	protected function patchRequest(...$args): array
200
	{
201
		return $this->request('PATCH', ...$args);
0 ignored issues
show
Documentation introduced by
$args is of type array<integer,array>, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
202
	}
203
204
	/**
205
	 * Remove some boilerplate for post requests
206
	 *
207
	 * @param array $args
208
	 * @return array
209
	 */
210 View Code Duplication
	protected function postRequest(...$args): array
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
211
	{
212
		$logger = null;
213
		if ($this->getContainer())
0 ignored issues
show
Bug introduced by
It seems like getContainer() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
214
		{
215
			$logger = $this->container->getLogger('kitsu-request');
216
		}
217
218
		$response = $this->getResponse('POST', ...$args);
0 ignored issues
show
Documentation introduced by
$args is of type array<integer,array>, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
219
		$validResponseCodes = [200, 201];
220
221
		if ( ! in_array((int) $response->getStatusCode(), $validResponseCodes))
222
		{
223
			if ($logger)
224
			{
225
				$logger->warning('Non 201 response for POST api call', $response->getBody());
226
			}
227
		}
228
229
		return JSON::decode($response->getBody(), TRUE);
230
	}
231
232
	/**
233
	 * Remove some boilerplate for delete requests
234
	 *
235
	 * @param array $args
236
	 * @return bool
237
	 */
238
	protected function deleteRequest(...$args): bool
239
	{
240
		$response = $this->getResponse('DELETE', ...$args);
0 ignored issues
show
Documentation introduced by
$args is of type array<integer,array>, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
241
		return ((int) $response->getStatusCode() === 204);
242
	}
243
}