Completed
Push — master ( 03bd4d...8fea20 )
by Nikola
01:56
created

NbsBrowser::getXmlDocument()   B

Complexity

Conditions 5
Paths 1

Size

Total Lines 27
Code Lines 23

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 21
CRAP Score 5

Importance

Changes 2
Bugs 0 Features 1
Metric Value
c 2
b 0
f 1
dl 0
loc 27
ccs 21
cts 21
cp 1
rs 8.439
cc 5
eloc 23
nc 1
nop 2
crap 5
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) 2016 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\Util;
13
14
use GuzzleHttp\Client;
15
use GuzzleHttp\Cookie\CookieJar;
16
use Psr\Http\Message\StreamInterface;
17
use Symfony\Component\DomCrawler\Crawler;
18
19
/**
20
 * Class NbsBrowser
21
 *
22
 * Browser browses trough web site of National Bank of Serbia and fetches documents with rates.
23
 *
24
 * @package RunOpenCode\ExchangeRate\NationalBankOfSerbia\Util
25
 */
26
class NbsBrowser
27
{
28
    const SOURCE = 'http://www.nbs.rs/kursnaListaModul/naZeljeniDan.faces';
29
    /**
30
     * @var Client
31
     */
32
    private $guzzleClient;
33
34
    /**
35
     * @var CookieJar
36
     */
37
    private $guzzleCookieJar;
38
39
    /**
40
     * Get XML document with rates.
41
     *
42
     * @param \DateTime $date
43
     * @param string $rateType
44
     * @return StreamInterface
45
     */
46 6
    public function getXmlDocument(\DateTime $date, $rateType)
47
    {
48 6
        return $this->request('POST', array(), array(
49 6
            'index:brKursneListe:' => '',
50 6
            'index:year' => $date->format('Y'),
51 6
            'index:inputCalendar1' => $date->format('d/m/Y'),
52 6
            'index:vrsta' => call_user_func(function($rateType) {
53
                switch ($rateType) {
54 6
                    case 'foreign_exchange_buying':     // FALL TROUGH
55 6
                    case 'foreign_exchange_selling':
56 2
                        return 1;
57
                        break;
1 ignored issue
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
58 4
                    case 'foreign_cash_buying':        // FALL TROUGH
59 4
                    case 'foreign_cash_selling':
60 2
                        return 2;
61
                        break;
1 ignored issue
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
62 2
                    default:
63 2
                        return 3;
64
                        break;
1 ignored issue
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
65 2
                }
66 6
            }, $rateType),
67 6
            'index:prikaz' => 3, // XML
68 6
            'index:buttonShow' => 'Show',
69 6
            'index' => 'index',
70 6
            'com.sun.faces.VIEW' => $this->getFormCsrfToken()
71 6
        ));
72
    }
73
74
    /**
75
     * Execute HTTP request and get raw body response.
76
     *
77
     * @param string $method HTTP Method.
78
     * @param array $params Params to send with request.
79
     * @return StreamInterface
80
     */
81 6
    private function request($method, array $query = array(), array $params = array())
82
    {
83 6
        $client = $this->getGuzzleClient();
84
85 6
        $response = $client->request($method, self::SOURCE, array(
86 6
            'cookies' => $this->getGuzzleCookieJar(),
87 6
            'form_params' => $params,
88
            'query' => $query
89 6
        ));
90
91 6
        return $response->getBody();
92
    }
93
94
    /**
95
     * Get NBS's form CSRF token.
96
     *
97
     * @return string CSRF token.
98
     *
99
     * @throws \RuntimeException When API is changed.
100
     */
101 6
    private function getFormCsrfToken()
102
    {
103 6
        $crawler = new Crawler($this->request('GET')->getContents());
104
105 6
        $hiddens = $crawler->filter('input[type="hidden"]');
106
107
        /**
108
         * @var \DOMElement $hidden
109
         */
110 6
        foreach ($hiddens as $hidden) {
111
112 6
            if ($hidden->getAttribute('id') === 'com.sun.faces.VIEW') {
113 6
                return $hidden->getAttribute('value');
114
            }
115 6
        }
116
117
        throw new \RuntimeException('FATAL ERROR: National Bank of Serbia changed it\'s API, unable to extract token.');
118
    }
119
120
    /**
121
     * Get Guzzle Client.
122
     *
123
     * @return Client
124
     */
125 6
    private function getGuzzleClient()
126
    {
127 6
        if ($this->guzzleClient === null) {
128 6
            $this->guzzleClient = new Client(array('cookies' => true));
129 6
        }
130
131 6
        return $this->guzzleClient;
132
    }
133
134
    /**
135
     * Get Guzzle CookieJar.
136
     *
137
     * @return CookieJar
138
     */
139 6
    private function getGuzzleCookieJar()
140
    {
141 6
        if ($this->guzzleCookieJar === null) {
142 6
            $this->guzzleCookieJar = new CookieJar();
143 6
        }
144
145 6
        return $this->guzzleCookieJar;
146
    }
147
}
148