1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/** |
4
|
|
|
* Copyright (c) 2014, Gustavo Henrique Mascarenhas Machado |
5
|
|
|
* All rights reserved. |
6
|
|
|
* |
7
|
|
|
* Redistribution and use in source and binary forms, with or without |
8
|
|
|
* modification, are permitted provided that the following conditions are met: |
9
|
|
|
* |
10
|
|
|
* * Redistributions of source code must retain the above copyright notice, this |
11
|
|
|
* list of conditions and the following disclaimer. |
12
|
|
|
* |
13
|
|
|
* * Redistributions in binary form must reproduce the above copyright notice, |
14
|
|
|
* this list of conditions and the following disclaimer in the documentation |
15
|
|
|
* and/or other materials provided with the distribution. |
16
|
|
|
* |
17
|
|
|
* * Neither the name of CorreiosAPI nor the names of its |
18
|
|
|
* contributors may be used to endorse or promote products derived from |
19
|
|
|
* this software without specific prior written permission. |
20
|
|
|
* |
21
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
22
|
|
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
23
|
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
24
|
|
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE |
25
|
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
26
|
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
27
|
|
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
28
|
|
|
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, |
29
|
|
|
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
30
|
|
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
31
|
|
|
*/ |
32
|
|
|
|
33
|
|
|
namespace CorreiosAPI; |
34
|
|
|
|
35
|
|
|
use Exception; |
36
|
|
|
use InvalidArgumentException; |
37
|
|
|
use RuntimeException; |
38
|
|
|
use GuzzleHttp\Client as HttpClient; |
39
|
|
|
|
40
|
|
|
class Tracker |
41
|
|
|
{ |
42
|
|
|
/** |
43
|
|
|
* Webservice endpoint |
44
|
|
|
*/ |
45
|
|
|
const WEBSERVICE_URL = 'https://webservice.correios.com.br/service/rastro/Rastro.wsdl'; |
46
|
|
|
|
47
|
|
|
/** |
48
|
|
|
* Fetches a single package |
49
|
|
|
*/ |
50
|
|
|
const FETCH_SINGLE = 'L'; |
51
|
|
|
|
52
|
|
|
/** |
53
|
|
|
* Fetches an interval of packages |
54
|
|
|
*/ |
55
|
|
|
const FETCH_INTERVAL = 'F'; |
56
|
|
|
|
57
|
|
|
/** |
58
|
|
|
* The results mode. 'T' will return the entire package history. |
59
|
|
|
*/ |
60
|
|
|
const RESULT_MODE = 'T'; |
61
|
|
|
|
62
|
|
|
/** |
63
|
|
|
* Webservice username, supplied by Correios |
64
|
|
|
* @var string |
65
|
|
|
*/ |
66
|
|
|
protected $username; |
67
|
|
|
|
68
|
|
|
/** |
69
|
|
|
* Webservice password, supplied by Correios |
70
|
|
|
* @var string |
71
|
|
|
*/ |
72
|
|
|
protected $password; |
73
|
|
|
|
74
|
|
|
|
75
|
|
|
/** |
76
|
|
|
* Sets up user credentials |
77
|
|
|
* @param string $username Webservice username |
78
|
|
|
* @param string $password Webservice password |
79
|
|
|
*/ |
80
|
6 |
|
public function __construct($username, $password) |
81
|
|
|
{ |
82
|
6 |
|
$this->username = $username; |
83
|
6 |
|
$this->password = $password; |
84
|
6 |
|
} |
85
|
|
|
|
86
|
|
|
/** |
87
|
|
|
* Validates the format of a tracking number |
88
|
|
|
* @param string $trackingNumber The tracking number |
89
|
|
|
* @return boolean |
90
|
|
|
*/ |
91
|
6 |
|
protected function validTrackingNumber($trackingNumber) |
92
|
|
|
{ |
93
|
6 |
|
if (preg_match('/^[\D]{2}[\d]{9}[\D]{2}$/i', $trackingNumber)) { |
94
|
1 |
|
return true; |
95
|
|
|
} |
96
|
|
|
|
97
|
5 |
|
throw new InvalidArgumentException("{$trackingNumber} is not a valid tracking number in the format XX000000000YY."); |
98
|
|
|
} |
99
|
|
|
|
100
|
|
|
/** |
101
|
|
|
* Tracks one or more packages |
102
|
|
|
* @param mixed $trackingNumber A single tracking number or an array of tracking numbers |
103
|
|
|
* @return mixed |
104
|
|
|
*/ |
105
|
6 |
|
public function track($trackingNumber) |
106
|
|
|
{ |
107
|
6 |
|
if (is_array($trackingNumber)) { |
108
|
|
|
throw new \RuntimeException('Only a single tracking number can be processed at a time.'); |
109
|
|
|
} |
110
|
|
|
|
111
|
6 |
|
$trackingNumber = strtoupper($trackingNumber); |
112
|
|
|
|
113
|
6 |
|
$this->validTrackingNumber($trackingNumber); |
114
|
|
|
|
115
|
1 |
|
return $this->queryAPI($trackingNumber, self::FETCH_SINGLE); |
116
|
|
|
} |
117
|
|
|
|
118
|
|
|
/** |
119
|
|
|
* Queries the Correios webservice |
120
|
|
|
* @param string $trackingNumbers A collection of tracking numbers |
121
|
|
|
* @param string $fetchMode The fetch mode (for one or more tracking numbers) |
122
|
|
|
* @return mixed |
123
|
|
|
*/ |
124
|
1 |
|
protected function queryAPI($trackingNumbers, $fetchMode) |
125
|
|
|
{ |
126
|
|
|
$params = [ |
127
|
1 |
|
'usuario' => $this->username |
128
|
1 |
|
, 'senha' => $this->password |
129
|
1 |
|
, 'tipo' => $fetchMode |
130
|
1 |
|
, 'resultado' => self::RESULT_MODE |
131
|
1 |
|
, 'objetos' => $trackingNumbers |
132
|
1 |
|
, 'lingua' => '101' |
133
|
1 |
|
]; |
134
|
|
|
|
135
|
|
|
try { |
136
|
1 |
|
$client = new \SoapClient(self::WEBSERVICE_URL); |
137
|
1 |
|
$response = $client->buscaEventos($params); |
138
|
|
|
|
139
|
1 |
|
if (!$response || empty($response)) { |
140
|
|
|
return false; |
141
|
|
|
} |
142
|
|
|
|
143
|
1 |
|
return $this->processResponse($response); |
144
|
1 |
|
} catch (Exception $e) { |
145
|
1 |
|
throw new RuntimeException($e->getMessage()); |
146
|
|
|
} |
147
|
|
|
|
148
|
|
|
return false; |
|
|
|
|
149
|
|
|
} |
150
|
|
|
|
151
|
|
|
/** |
152
|
|
|
* Processes the webservice response and builds a readable associative |
153
|
|
|
* array of the events associated to one or more packages |
154
|
|
|
* @param string $responseBody The response body (xml) |
155
|
|
|
* @return mixed |
156
|
|
|
*/ |
157
|
1 |
|
protected function processResponse($responseBody) |
158
|
|
|
{ |
159
|
1 |
|
$response = $responseBody->{'return'}; |
160
|
|
|
|
161
|
1 |
|
if (!$response || $response->objeto->erro) { |
162
|
|
|
return false; |
163
|
|
|
} |
164
|
|
|
|
165
|
|
|
$results = []; |
166
|
|
|
$events = []; |
167
|
|
|
|
168
|
|
|
foreach ($response->objeto->evento as $event) { |
169
|
|
|
$events[] = [ |
170
|
|
|
'when' => $event->data . ' ' . $event->hora |
171
|
|
|
, 'where' => strtoupper($event->local . (strlen($event->cidade) ? (' - ' . $event->cidade . '/' . $event->uf) : '')) |
172
|
|
|
, 'action' => Tracker\ResponseCodes::getMessage($event->tipo, $event->status) |
173
|
|
|
, 'details' => (string) $event->descricao |
174
|
|
|
]; |
175
|
|
|
} |
176
|
|
|
|
177
|
|
|
$trackingNumber = (string) $response->objeto->numero; |
178
|
|
|
$results[$trackingNumber] = $events; |
179
|
|
|
|
180
|
|
|
return $results; |
181
|
|
|
} |
182
|
|
|
} |
183
|
|
|
|
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
orexit
statements that have been added for debug purposes.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.