Completed
Push — master ( e2180c...7975fa )
by Carsten
02:57 queued 11s
created

GuzzleGeoDataFactory::fromString()   B

Complexity

Conditions 5
Paths 7

Size

Total Lines 56

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 28
CRAP Score 5

Importance

Changes 0
Metric Value
dl 0
loc 56
c 0
b 0
f 0
ccs 28
cts 28
cp 1
rs 8.6488
cc 5
nc 7
nop 1
crap 5

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
namespace Germania\GeoData;
3
4
use GuzzleHttp\Client as GuzzleClient;
5
use GuzzleHttp\Exception\RequestException;
6
use GuzzleHttp\Exception\ClientException;
7
use GuzzleHttp\Psr7;
8
use Germania\GeoData\GeoData;
9
use Germania\GeoData\GeoDataFactory;
10
use Psr\Log\LoggerInterface;
11
use Psr\Log\LoggerAwareInterface;
12
use Psr\Log\LoggerAwareTrait;
13
use Psr\Log\NullLogger;
14
15
16
class GuzzleGeoDataFactory implements LoggerAwareInterface
17
{
18
19
	use LoggerAwareTrait;
20
21
	/**
22
	 * @var \GuzzleHttp\Client
23
	 */
24
	public $http_client;
25
26
27
	/**
28
	 * @var string
29
	 */
30
	public $url_path = "coordinates";
31
32
33
	/**
34
	 * @var GeoDataFactory
35
	 */
36
	public $geodata_factory;
37
38
	/**
39
	 * @var string
40
	 */
41
	public $request_exception_loglevel = "error";
42
43
	/**
44
	 * @var string
45
	 */
46
	public $client_exception_loglevel = "error";
47
48
49
	/**
50
	 * @param GuzzleClient    $http_client Guzzle Client, configured for Germania's GeoCoder API
51
	 * @param LoggerInterface $logger          PSR-3 Logger
52
	 */
53 24
	public function __construct(GuzzleClient $http_client, LoggerInterface $logger = null)
54
	{
55 24
		$this->http_client = $http_client;
56 24
		$this->geodata_factory = new GeoDataFactory;
57 24
		$this->setLogger($logger ?: new NullLogger);
58 24
	}
59
60
61
	/**
62
	 * @param string $loglevel PSR-3 Loglevel name
63
	 */
64 2
	public function setRequestExceptionLoglevel( string $loglevel )
65
	{
66 2
		$this->request_exception_loglevel = $loglevel;
67 2
		return $this;
68
	}
69
70
	/**
71
	 * @param string $loglevel PSR-3 Loglevel name
72
	 */
73 2
	public function setClientRxceptionLoglevel( string $loglevel )
74
	{
75 2
		$this->client_exception_loglevel = $loglevel;
76 2
		return $this;
77
	}
78
79
80
	/**
81
	 * @param  string $location [description]
82
	 * @return GeoData
83
	 * @throws \RuntimeException
84
	 */
85 22
	public function fromString( string $location ) : GeoData
86
	{
87
		try {
88
			// Guzzle client returns ResponseInterface!
89 22
			$response = $this->http_client->get( $this->url_path, [
90 22
				"query" => ['search' => $location]
91
			]);
92
		}
93 8
		catch (ClientException $e) {
94 6
		    $e_response = $e->getResponse();
95 6
		    $e_status = $e_response->getStatusCode();
96
97 6
			$msg = sprintf("Client-side error %s on Geocoder API request: %s", $e_status, $e->getMessage());
98 6
			$this->logger->log( $this->client_exception_loglevel, $msg, [
99 6
				'exception' => get_class($e)
100
			]);
101
102
			switch ($e_status):
103 6
				case 404: 
104 4
					throw new GeoDataFactoryNotFoundException($msg, 0, $e);
105
					break;
0 ignored issues
show
Unused Code introduced by
break; does not seem to be reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
106
				default:
107 2
					throw new GeoDataFactoryRuntimeException($msg, 0, $e);
108
					break;
0 ignored issues
show
Unused Code introduced by
break; does not seem to be reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
109
			endswitch;
110
		}		
111 2
		catch (RequestException $e) {
112 2
			$msg = sprintf("Request-related error on Geocoder API request: %s", $e->getMessage());
113 2
			$this->logger->log( $this->request_exception_loglevel, $msg, [
114 2
				'exception' => get_class($e)
115
			]);
116 2
			throw new GeoDataFactoryRuntimeException($msg, 0, $e);
117
		}		
118
119
120
121
		try {
122 14
			$response_body = $response->getBody();
123 14
			$response_body_decoded = json_decode($response_body, "associative");
124 14
			$this->validateDecodedResponse( $response_body_decoded );	
125
		}
126 12
		catch (\Throwable $e) {
127 12
			$msg = sprintf("Error on Geocoder API response validation: %s", $e->getMessage());			
128 12
			$this->logger->log( "error", $msg, [
129 12
				'exception' => get_class($e)
130
			]);
131 12
			throw new GeoDataFactoryRuntimeException($msg, 0, $e);
132
		}
133
134
			
135 2
		$coordinates_raw = $response_body_decoded['data']["attributes"];
136 2
		$geodata = $this->geodata_factory->fromArray( $coordinates_raw );
137
138
139 2
		return $geodata;
140
	}
141
142
143
144
145
	/**
146
	 * Validates the decoded response, throwing things in error case.
147
	 * 
148
	 * @param  mixed $response_body_decoded
149
	 * @return void
150
	 *
151
	 * @throws UnexpectedValueException
152
	 */
153 14
	protected function validateDecodedResponse( $response_body_decoded )
154
	{
155
		// "data" is quite common in JsonAPI responses, 
156
		// however, we need it as array.
157
158 14
		if (!is_array( $response_body_decoded )):
159 4
			throw new \UnexpectedValueException("GeocoderAPI response: Expected array");
160
		endif;
161
162 10
		if (!isset( $response_body_decoded['data'] )):
163 2
			throw new \UnexpectedValueException("GeocoderAPI response: Missing 'data' element");
164
		endif;
165
166 8
		if (!is_array( $response_body_decoded['data'] )):
167 2
			throw new \UnexpectedValueException("GeocoderAPI response: Element 'data' is not array");
168
		endif;
169
170 6
		if (!isset( $response_body_decoded['data']["attributes"] )):
171 2
			throw new \UnexpectedValueException("GeocoderAPI response: Missing 'data.attributes' element");
172
		endif;
173
174 4
		if (!is_array( $response_body_decoded['data']["attributes"] )):
175 2
			throw new \UnexpectedValueException("GeocoderAPI response: Element 'data.attributes' is not array");
176
		endif;
177
178
	}		
179
}