1
|
|
|
<?php |
2
|
|
|
/* |
3
|
|
|
* This file is part of the Exchange Rate package, an RunOpenCode project. |
4
|
|
|
* |
5
|
|
|
* (c) 2016 RunOpenCode |
6
|
|
|
* |
7
|
|
|
* For the full copyright and license information, please view the LICENSE |
8
|
|
|
* file that was distributed with this source code. |
9
|
|
|
*/ |
10
|
|
|
namespace RunOpenCode\ExchangeRate\Source\Api; |
11
|
|
|
|
12
|
|
|
use RunOpenCode\ExchangeRate\Contract\RateInterface; |
13
|
|
|
use RunOpenCode\ExchangeRate\Model\Rate; |
14
|
|
|
use RunOpenCode\ExchangeRate\Source\NationalBankOfSerbiaDomCrawlerSource; |
15
|
|
|
|
16
|
|
|
/** |
17
|
|
|
* Class NationalBankOfSerbiaXmlSaxParser |
18
|
|
|
* |
19
|
|
|
* Parses XML files from National bank of Serbia. |
20
|
|
|
* |
21
|
|
|
* @package RunOpenCode\ExchangeRate\Source\Api |
22
|
|
|
* @internal |
23
|
|
|
*/ |
24
|
|
|
class NationalBankOfSerbiaXmlSaxParser |
25
|
|
|
{ |
26
|
|
|
/** |
27
|
|
|
* @var RateInterface[] |
28
|
|
|
*/ |
29
|
|
|
private $rates; |
30
|
|
|
|
31
|
|
|
/** |
32
|
|
|
* @var \SplStack |
33
|
|
|
*/ |
34
|
|
|
private $stack; |
35
|
|
|
|
36
|
|
|
/** |
37
|
|
|
* @var array |
38
|
|
|
*/ |
39
|
|
|
private $currentRate; |
40
|
|
|
|
41
|
|
|
/** |
42
|
|
|
* @var \DateTime |
43
|
|
|
*/ |
44
|
|
|
private $date; |
45
|
|
|
|
46
|
|
|
/** |
47
|
|
|
* @var string |
48
|
|
|
*/ |
49
|
|
|
private $rateType; |
50
|
|
|
|
51
|
|
|
/** |
52
|
|
|
* Parse XML from National bank of Serbia. |
53
|
|
|
* |
54
|
|
|
* @param string $xml |
55
|
|
|
* @return RateInterface[] |
56
|
|
|
*/ |
57
|
2 |
|
public function parse($xml) |
58
|
|
|
{ |
59
|
2 |
|
$this->rates = array(); |
60
|
2 |
|
$this->stack = new \SplStack(); |
61
|
2 |
|
$this->currentRate = array(); |
62
|
2 |
|
$this->date = new \DateTime('now'); |
63
|
2 |
|
$this->rateType = 'default'; |
64
|
|
|
|
65
|
2 |
|
$parser = xml_parser_create(); |
66
|
|
|
|
67
|
2 |
|
xml_set_element_handler( |
68
|
2 |
|
$parser, |
69
|
|
|
\Closure::bind(function($parser, $name, $attributes) { |
70
|
2 |
|
$this->onStart($parser, $name, $attributes); |
71
|
2 |
|
}, $this), |
72
|
|
|
\Closure::bind(function($parser, $name) { |
73
|
2 |
|
$this->onEnd($parser, $name); |
74
|
2 |
|
}, $this) |
75
|
2 |
|
); |
76
|
|
|
|
77
|
2 |
|
xml_set_character_data_handler( |
78
|
2 |
|
$parser, |
79
|
|
|
\Closure::bind(function($parser, $data) { |
80
|
2 |
|
$this->onData($parser, $data); |
81
|
2 |
|
}, $this)); |
82
|
|
|
|
83
|
2 |
|
xml_parse($parser, $xml); |
84
|
2 |
|
xml_parser_free($parser); |
85
|
|
|
|
86
|
2 |
|
return $this->rates; |
87
|
|
|
} |
88
|
|
|
|
89
|
|
|
/** |
90
|
|
|
* SAX on tag start callback. |
91
|
|
|
* |
92
|
|
|
* @param $parser |
93
|
|
|
* @param $name |
94
|
|
|
* @param $attributes |
95
|
|
|
*/ |
96
|
2 |
|
protected function onStart($parser, $name, $attributes) |
97
|
|
|
{ |
98
|
2 |
|
$this->stack->push($name); |
99
|
|
|
|
100
|
2 |
|
if ($name === 'ITEM') { |
101
|
2 |
|
$this->currentRate = array(); |
102
|
2 |
|
} |
103
|
2 |
|
} |
104
|
|
|
|
105
|
|
|
/** |
106
|
|
|
* SAX on tag end callback. |
107
|
|
|
* |
108
|
|
|
* @param $parser |
109
|
|
|
* @param $name |
110
|
|
|
*/ |
111
|
2 |
|
protected function onEnd($parser, $name) |
112
|
|
|
{ |
113
|
2 |
|
$this->stack->pop(); |
114
|
|
|
|
115
|
2 |
|
$buildRate = function($value, $currencyCode, $rateType, $date) { |
116
|
|
|
|
117
|
2 |
|
return new Rate( |
118
|
2 |
|
NationalBankOfSerbiaDomCrawlerSource::NAME, |
119
|
2 |
|
$value, |
120
|
2 |
|
$currencyCode, |
121
|
2 |
|
$rateType, |
122
|
2 |
|
$date, |
123
|
2 |
|
'RSD', |
124
|
2 |
|
new \DateTime('now'), |
125
|
2 |
|
new \DateTime('now') |
126
|
2 |
|
); |
127
|
2 |
|
}; |
128
|
|
|
|
129
|
2 |
|
if ($name === 'ITEM') { |
130
|
|
|
|
131
|
2 |
View Code Duplication |
if (array_key_exists('buyingRate', $this->currentRate)) { |
|
|
|
|
132
|
|
|
|
133
|
|
|
$this->rates[] = $buildRate( |
134
|
|
|
$this->currentRate['buyingRate'] / $this->currentRate['unit'], |
135
|
|
|
$this->currentRate['currencyCode'], |
136
|
|
|
$this->rateType . '_buying', |
137
|
|
|
$this->date |
138
|
|
|
); |
139
|
|
|
} |
140
|
|
|
|
141
|
2 |
View Code Duplication |
if (array_key_exists('sellingRate', $this->currentRate)) { |
|
|
|
|
142
|
|
|
|
143
|
|
|
$this->rates[] = $buildRate( |
144
|
|
|
$this->currentRate['sellingRate'] / $this->currentRate['unit'], |
145
|
|
|
$this->currentRate['currencyCode'], |
146
|
|
|
$this->rateType . '_selling', |
147
|
|
|
$this->date |
148
|
|
|
); |
149
|
|
|
} |
150
|
|
|
|
151
|
2 |
|
if (array_key_exists('middleRate', $this->currentRate)) { |
152
|
|
|
|
153
|
2 |
|
$this->rates[] = $buildRate( |
154
|
2 |
|
$this->currentRate['middleRate'] / $this->currentRate['unit'], |
155
|
2 |
|
$this->currentRate['currencyCode'], |
156
|
2 |
|
'default', |
157
|
2 |
|
$this->date |
158
|
2 |
|
); |
159
|
2 |
|
} |
160
|
|
|
|
161
|
2 |
|
$this->currentRate = array(); |
162
|
2 |
|
} |
163
|
2 |
|
} |
164
|
|
|
|
165
|
|
|
/** |
166
|
|
|
* SAX on data callback. |
167
|
|
|
* |
168
|
|
|
* @param $parser |
169
|
|
|
* @param $data |
170
|
|
|
*/ |
171
|
2 |
|
protected function onData($parser, $data) |
172
|
|
|
{ |
173
|
2 |
|
if (!empty($data)) { |
174
|
|
|
|
175
|
2 |
|
switch ($this->stack->top()) { |
176
|
2 |
|
case 'DATE': |
177
|
2 |
|
$this->date = \DateTime::createFromFormat('d.m.Y', $data); |
178
|
2 |
|
break; |
179
|
2 |
|
case 'TYPE': |
180
|
2 |
|
$data = trim($data); |
181
|
2 |
|
if ($data === 'FOREIGN EXCHANGE') { |
182
|
|
|
$this->rateType = 'foreign_exchange'; |
183
|
2 |
|
} elseif ($data === 'FOREIGN CASH') { |
184
|
|
|
$this->rateType = 'foreign_cache'; |
185
|
|
|
} |
186
|
2 |
|
break; |
187
|
2 |
|
case 'CURRENCY': |
188
|
2 |
|
$this->currentRate['currencyCode'] = trim($data); |
189
|
2 |
|
break; |
190
|
2 |
|
case 'UNIT': |
191
|
2 |
|
$this->currentRate['unit'] = (int) trim($data); |
192
|
2 |
|
break; |
193
|
2 |
|
case 'BUYING_RATE': |
194
|
|
|
$this->currentRate['buyingRate'] = (float) trim($data); |
195
|
|
|
break; |
196
|
2 |
|
case 'SELLING_RATE': |
197
|
|
|
$this->currentRate['sellingRate'] = (float) trim($data); |
198
|
|
|
break; |
199
|
2 |
|
case 'MIDDLE_RATE': |
200
|
2 |
|
$this->currentRate['middleRate'] = (float) trim($data); |
201
|
2 |
|
break; |
202
|
2 |
|
} |
203
|
2 |
|
} |
204
|
2 |
|
} |
205
|
|
|
|
206
|
|
|
/** |
207
|
|
|
* Parse XML from National bank of Serbia. |
208
|
|
|
* |
209
|
|
|
* @param string $xml |
210
|
|
|
* @return RateInterface[] |
211
|
|
|
*/ |
212
|
2 |
|
public static function parseXml($xml) |
213
|
|
|
{ |
214
|
2 |
|
$parser = new static(); |
215
|
2 |
|
return $parser->parse($xml); |
216
|
|
|
} |
217
|
|
|
|
218
|
|
|
} |
219
|
|
|
|
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.