Completed
Push — master ( 3b7a76...bed01b )
by Nikola
06:06
created

XmlParser::getResult()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
cc 1
eloc 2
nc 1
nop 0
crap 1
1
<?php
2
/*
3
 * This file is part of the Exchange Rate package, an RunOpenCode project.
4
 *
5
 * Implementation of exchange rate crawler for National Bank of Serbia, http://www.nbs.rs.
6
 *
7
 * (c) 2017 RunOpenCode
8
 *
9
 * For the full copyright and license information, please view the LICENSE
10
 * file that was distributed with this source code.
11
 */
12
namespace RunOpenCode\ExchangeRate\NationalBankOfSerbia\Parser;
13
14
use RunOpenCode\ExchangeRate\Contract\RateInterface;
15
use RunOpenCode\ExchangeRate\Model\Rate;
16
use RunOpenCode\ExchangeRate\NationalBankOfSerbia\Api;
17
use RunOpenCode\ExchangeRate\NationalBankOfSerbia\Enum\RateType;
18
use RunOpenCode\ExchangeRate\NationalBankOfSerbia\Exception\RuntimeException;
19
use RunOpenCode\Sax\Handler\AbstractSaxHandler;
20
21
/**
22
 * Class XmlParser
23
 *
24
 * Parse XML document with daily rates from National Bank of Serbia.
25
 *
26
 * @package RunOpenCode\ExchangeRate\NationalBankOfSerbia\Parser
27
 */
28
class XmlParser extends AbstractSaxHandler
29
{
30
    /**
31
     * @var RateInterface[]
32
     */
33
    private $rates;
34
35
    /**
36
     * @var \SplStack
37
     */
38
    private $stack;
39
40
    /**
41
     * @var array
42
     */
43
    private $currentRate;
44
45
    /**
46
     * @var \DateTime
47
     */
48
    private $date;
49
50
    /**
51
     * @var string
52
     */
53
    private $rateType;
54
55
    /**
56
     * {@inheritdoc}
57
     */
58 9
    protected function onDocumentStart($parser, $stream)
59
    {
60 9
        $this->rates = array();
61 9
        $this->stack = new \SplStack();
62 9
        $this->currentRate = array();
63 9
        $this->date = new \DateTime('now');
64 9
        $this->rateType = RateType::MEDIAN;
65 9
    }
66
67
    /**
68
     * {@inheritdoc}
69
     */
70 9
    protected function onElementStart($parser, $name, $attributes)
71
    {
72 9
        $this->stack->push($name);
73
74 9
        if ($name === 'ITEM') {
75 9
            $this->currentRate = array();
76
        }
77 9
    }
78
79
    /**
80
     * {@inheritdoc}
81
     */
82 9
    protected function onElementData($parser, $data)
83
    {
84 9
        if (!empty($data)) {
85
86 9
            switch ($this->stack->top()) {
87 9
                case 'DATE':
88 9
                    $this->date = \DateTime::createFromFormat('d.m.Y', $data);
1 ignored issue
show
Documentation Bug introduced by
It seems like \DateTime::createFromFormat('d.m.Y', $data) can also be of type false. However, the property $date is declared as type object<DateTime>. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
89 9
                    break;
90 9
                case 'TYPE':
91 9
                    $data = trim($data);
92 9
                    if ($data === 'FOREIGN EXCHANGE') {
93 3
                        $this->rateType = 'foreign_exchange';
94 6
                    } elseif ($data === 'FOREIGN CASH') {
95 3
                        $this->rateType = 'foreign_cash';
96
                    }
97 9
                    break;
98 9
                case 'CURRENCY':
99 9
                    $this->currentRate['currencyCode'] = trim($data);
100 9
                    break;
101 9
                case 'UNIT':
102 9
                    $this->currentRate['unit'] = (int) trim($data);
103 9
                    break;
104 9
                case 'BUYING_RATE':
105 6
                    $this->currentRate['buyingRate'] = (float) trim($data);
106 6
                    break;
107 9
                case 'SELLING_RATE':
108 6
                    $this->currentRate['sellingRate'] = (float) trim($data);
109 6
                    break;
110 9
                case 'MIDDLE_RATE':
111 3
                    $this->currentRate['middleRate'] = (float) trim($data);
112 3
                    break;
113
            }
114
        }
115 9
    }
116
117
    /**
118
     * {@inheritdoc}
119
     */
120 9
    protected function onElementEnd($parser, $name)
121
    {
122 9
        $this->stack->pop();
123
124 9
        $buildRate = function($value, $currencyCode, $rateType, $date) {
125
126 9
            return new Rate(
127 9
                Api::NAME,
128
                $value,
129
                $currencyCode,
130
                $rateType,
131
                $date,
132 9
                'RSD',
133 9
                new \DateTime('now'),
134 9
                new \DateTime('now')
135
            );
136 9
        };
137
138 9
        if ($name === 'ITEM') {
139
140 9
            if (array_key_exists('buyingRate', $this->currentRate)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
141
142 6
                $this->rates[] = $buildRate(
143 6
                    $this->currentRate['buyingRate'] / $this->currentRate['unit'],
144 6
                    $this->currentRate['currencyCode'],
145 6
                    $this->rateType . '_buying',
146 6
                    $this->date
147
                );
148
            }
149
150 9
            if (array_key_exists('sellingRate', $this->currentRate)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
151
152 6
                $this->rates[] = $buildRate(
153 6
                    $this->currentRate['sellingRate'] / $this->currentRate['unit'],
154 6
                    $this->currentRate['currencyCode'],
155 6
                    $this->rateType . '_selling',
156 6
                    $this->date
157
                );
158
            }
159
160 9
            if (array_key_exists('middleRate', $this->currentRate)) {
161
162 3
                $this->rates[] = $buildRate(
163 3
                    $this->currentRate['middleRate'] / $this->currentRate['unit'],
164 3
                    $this->currentRate['currencyCode'],
165 3
                    RateType::MEDIAN,
166 3
                    $this->date
167
                );
168
            }
169
170 9
            $this->currentRate = array();
171
        }
172 9
    }
173
174
    /**
175
     * {@inheritdoc}
176
     */
177 9
    protected function onDocumentEnd($parser, $stream)
178
    {
179
        // noop
180 9
    }
181
182
    /**
183
     * {@inheritdoc}
184
     *
185
     * @throws \RuntimeException
186
     */
187
    protected function onParseError($message, $code, $lineno)
188
    {
189
        throw new RuntimeException(sprintf('Unable to parse XML source from National Bank of Serbia, reason: "%s", lineno: "%s".', $message, $lineno), $code);
190
    }
191
192
    /**
193
     * {@inheritdoc}
194
     */
195 9
    protected function getResult()
196
    {
197 9
        return $this->rates;
198
    }
199
}
200