DHLECommerceTracker   A
last analyzed

Complexity

Total Complexity 22

Size/Duplication

Total Lines 161
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 10

Importance

Changes 0
Metric Value
wmc 22
lcom 1
cbo 10
dl 0
loc 161
rs 10
c 0
b 0
f 0

8 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 5 2
A setAccessToken() 0 4 2
B track() 0 25 4
B getAccessToken() 0 22 5
A getCredential() 0 4 1
A getHttpClient() 0 7 2
A sendRequest() 0 9 2
B buildShipment() 0 23 4
1
<?php
2
/**
3
 * Slince shipment tracker library
4
 * @author Tao <[email protected]>
5
 */
6
namespace Slince\ShipmentTracking\DHLECommerce;
7
8
use GuzzleHttp\Client as HttpClient;
9
use GuzzleHttp\Exception\GuzzleException;
10
use GuzzleHttp\Psr7\Request;
11
use Psr\Http\Message\RequestInterface;
12
use Slince\ShipmentTracking\DHLECommerce\Exception\InvalidAccessTokenException;
13
use Slince\ShipmentTracking\Shipment;
14
use Slince\ShipmentTracking\ShipmentEvent;
15
use Slince\ShipmentTracking\HttpAwareTracker;
16
use Slince\ShipmentTracking\Exception\TrackException;
17
18
class DHLECommerceTracker extends HttpAwareTracker
19
{
20
    /**
21
     * @var string
22
     */
23
    const ACCESS_TOKEN_ENDPOINT = 'https://api.dhlecommerce.dhl.com/rest/v1/OAuth/AccessToken';
24
25
    /**
26
     * @var string
27
     */
28
    const TRACKING_ENDPOINT = 'https://api.dhlecommerce.dhl.com/rest/v2/Tracking';
29
30
    /**
31
     * @var Credential
32
     */
33
    protected $credential;
34
35
    /**
36
     * @var AccessToken
37
     */
38
    protected $accessToken;
39
40
    public function __construct($clientId, $password, HttpClient $httpClient = null)
41
    {
42
        $httpClient && $this->setHttpClient($httpClient);
43
        $this->credential = new Credential($clientId, $password);
44
    }
45
46
    /**
47
     * Sets the access token for the tracker
48
     * @param string|AccessToken $accessToken
49
     */
50
    public function setAccessToken($accessToken)
51
    {
52
        $this->accessToken = $accessToken instanceof AccessToken ? $accessToken : new AccessToken($accessToken);
53
    }
54
55
    /**
56
     * {@inheritdoc}
57
     */
58
    public function track($trackingNumber)
59
    {
60
        $parameters = [
61
            'trackItemRequest' => [
62
                'token' => $this->getAccessToken()->getToken(),
63
                'messageLanguage' => 'en',
64
                'messageVersion' => '1.1',
65
                'trackingReferenceNumber' => [$trackingNumber]
66
            ]
67
        ];
68
        $request = new Request('POST', static::TRACKING_ENDPOINT);
69
        $json = $this->sendRequest($request, [
70
            'json' => $parameters
71
        ]);
72
        if ($json['trackItemResponse']['responseCode'] != 0 &&
73
            stripos($json['trackItemResponse']['responseText'], 'Invalid access token') !== false
74
        ) {
75
            throw new InvalidAccessTokenException(sprintf('The access token "%s" is invalid, please refresh for the new one', $this->accessToken));
76
        }
77
        if ($json['trackItemResponse']['responseCode'] != 0) {
78
            throw new TrackException(sprintf('Bad response with code "%d"', $json['trackItemResponse']['responseCode']));
79
        }
80
        return static::buildShipment($json);
81
82
    }
83
84
    /**
85
     * Gets the access token
86
     * @param boolean $refresh
87
     * @throws TrackException
88
     * @return AccessToken
89
     */
90
    public function getAccessToken($refresh = false)
91
    {
92
        if ($this->accessToken && !$refresh) {
93
            return $this->accessToken;
94
        }
95
        $request = new Request('GET', static::ACCESS_TOKEN_ENDPOINT);
96
        $json = $this->sendRequest($request, [
97
            'query' => array_merge($this->credential->toArray(), [
98
                'returnFormat' => 'json'
99
            ])
100
        ]);
101
        if (!isset($json['accessTokenResponse']['responseStatus']) || $json['accessTokenResponse']['responseStatus']['code'] != '100000') {
102
            throw new TrackException(sprintf('Get access token error with message details "%s"',
103
                $json['accessTokenResponse']['responseStatus']['messageDetails']
104
            ));
105
        }
106
        return $this->accessToken = new AccessToken(
107
            $json['accessTokenResponse']['token'],
108
            $json['accessTokenResponse']['token_type'],
109
            $json['accessTokenResponse']['expires_in_seconds']
110
        );
111
    }
112
113
    /**
114
     * @return Credential
115
     */
116
    public function getCredential()
117
    {
118
        return $this->credential;
119
    }
120
121
    /**
122
     * @return HttpClient
123
     * @codeCoverageIgnore
124
     */
125
    protected function getHttpClient()
126
    {
127
        if (!is_null($this->httpClient)) {
128
            return $this->httpClient;
129
        }
130
        return $this->httpClient = new HttpClient();
131
    }
132
133
    /**
134
     * @param RequestInterface $request
135
     * @param array $options
136
     * @throws TrackException
137
     * @return array
138
     * @codeCoverageIgnore
139
     */
140
    protected function sendRequest(RequestInterface $request, $options = [])
141
    {
142
        try {
143
            $response = $this->getHttpClient()->send($request, $options);
144
        } catch (GuzzleException $exception) {
145
            throw new TrackException($exception->getMessage());
146
        }
147
        return \GuzzleHttp\json_decode((string)$response->getBody(), true);
148
    }
149
150
    /**
151
     * @param array $json
152
     * @throws TrackException
153
     * @return Shipment
154
     */
155
    protected static function buildShipment($json)
156
    {
157
        $json = reset($json['trackItemResponse']['items']);
158
        if (!$json) {
159
            throw new TrackException(sprintf('Bad response'));
160
        }
161
        $events = array_map(function($item) {
162
            return ShipmentEvent::fromArray([
163
                'location' => $item['address']['city'],
164
                'description' => $item['description'],
165
                'date' => $item['timestamp'],
166
                'status' => $item['status']
167
            ]);
168
        }, $json['events']);
169
        $shipment = new Shipment($events);
170
        $isDelivered = ($lastEvent = end($events)) ? $lastEvent->getStatus() == 71093 : null;
171
        $shipment->setIsDelivered($isDelivered)
0 ignored issues
show
Bug introduced by
It seems like $isDelivered defined by ($lastEvent = end($event...tatus() == 71093 : null on line 170 can also be of type null; however, Slince\ShipmentTracking\Shipment::setIsDelivered() does only seem to accept boolean, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
172
            ->setDestination($json['destination']['countryCode']);
173
        if ($firstEvent = reset($events)) {
174
            $shipment->setDeliveredAt($firstEvent->getDate());
175
        }
176
        return $shipment;
177
    }
178
}