Quandl::getSymbol()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 11
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 1

Importance

Changes 2
Bugs 0 Features 1
Metric Value
c 2
b 0
f 1
dl 0
loc 11
ccs 8
cts 8
cp 1
rs 9.4285
cc 1
eloc 7
nc 1
nop 2
crap 1
1
<?php
2
3
namespace Royopa\Quandl;
4
5
class Quandl
6
{
7
    public $api_key       = null;
8
    public $format        = null;
9
    public $cache_handler = null;
10
    public $was_cached    = false;
11
    public $force_curl    = false;
12
    public $no_ssl_verify = false; // disable ssl verification for curl
13
    public $last_url      = null;
14
    public $error         = null;
15
16
    private static $url_templates = [
17
        "symbol"  => 'https://www.quandl.com/api/v1/datasets/%s.%s?%s',
18
        "search"  => 'https://www.quandl.com/api/v1/datasets.%s?%s',
19
        "list"    => 'https://www.quandl.com/api/v2/datasets.%s?%s',
20
    ];
21
    
22 8
    public function __construct($api_key = null, $format = 'object')
23
    {
24 8
        $this->api_key = $api_key;
25 8
        $this->format  = $format;
26 8
    }
27
28
    // getSymbol returns data for a given symbol.
29 8
    public function getSymbol($symbol, $params = null)
30
    {
31 8
        $url = $this->getUrl(
32 8
            'symbol',
33 8
            $symbol,
34 8
            $this->getFormat(),
35 8
            $this->arrangeParams($params)
36 8
        );
37
38 8
        return $this->getData($url);
39
    }
40
41
    // getSearch returns results for a search query.
42
    // CSV output is not supported with this node so if format
43
    // is set to CSV, the result will fall back to object mode.
44
    public function getSearch($query, $page = 1, $per_page = 300)
45
    {
46
        $params = $this->constructParams($query, $page, $per_page);
47
        $url    = $this->generateUrl('search', true, $params);
48
49
        return $this->getData($url);
50
    }
51
52
    // getList returns the list of symbols for a given source.
53
    public function getList($query, $page = 1, $per_page = 300)
54
    {
55
        $params          = $this->constructParams($query, $page, $per_page);
56
        $params["query"] = "*";
57
        $url             = $this->generateUrl('list', false, $params);
58
59
        return $this->getData($url);
60
    }
61
62
    //generate the url basead in parameters
63
    public function generateUrl($type = '', $format = false, $params)
64
    {
65
        $url = $this->getUrl(
66
            $type,
67
            $this->getFormat($format),
68
            $this->arrangeParams($params)
69
        );
70
71
        return $url;
72
    }
73
74
    // getFormat returns one of the three formats supported by Quandl.
75
    // It is here for two reasons: 
76
    //  1) we also allow 'object' format. this will be sent to Quandl
77
    //     as "json" but the getData method will return a json_decoded
78
    //     output.
79
    //  2) some Quandl nodes do not support CSV (namely search).
80 8
    private function getFormat($omit_csv = false)
81
    {
82 8
        if (($this->format == 'csv' && $omit_csv) || $this->format == 'object') {
83 4
            return 'json';
84
        }
85
86 4
        return $this->format;
87
    }
88
89
    // getUrl receives a kind that points to a URL template and 
90
    // a variable number of parameters, which will be replaced
91
    // in the template.
92
    /**
93
     * @param string $kind
94
     */
95 8
    private function getUrl($kind)
96
    {
97 8
        $template       = self::$url_templates[$kind];
98 8
        $args           = array_slice(func_get_args(), 1);
99 8
        $this->last_url = trim(vsprintf($template, $args), '?&');
100
        
101 8
        return $this->last_url;
102
    }
103
104
    // getData executes the download operation and returns the result
105
    // as is, or json-decoded if 'object' type was requested.
106
    /**
107
     * @param string $url
108
     */
109 8
    private function getData($url)
110
    {
111 8
        $result = $this->executeDownload($url);
112
        
113 7
        if ($this->format == 'object') {
114 3
            return json_decode($result);
115
        }
116
117 4
        return $result;
118
    }
119
120
    // executeDownload gets a URL, and returns the downloaded document
121
    // either from cache (if cache_handler is set) or from Quandl.
122 8
    private function executeDownload($url)
123
    {
124 8
        if (! $this->cache_handler) {
125 7
            $data = $this->download($url);
126 7
            return $data;
127
        }
128
        
129 1
        $data = $this->attemptGetFromCache($url);
130
131 1
        if (!$data) {
132 1
            throw new \Exception('Error in download document');
133
        }
134
135
        return $data;
136
    }
137
138
    // attemptGetFromCache is called if a cache_handler is available.
139
    // It will call the cache handler with a get request, return the 
140
    // document if found, and will ask it to store the downloaded 
141
    // object where applicable.
142 1
    private function attemptGetFromCache($url)
143
    {
144 1
        $this->was_cached = false;
145 1
        $data = call_user_func($this->cache_handler, 'get', $url);
146
        
147 1
        if ($data) {
148
            $this->was_cached = true;
149
            return $data;
150
        }
151
152 1
        $data = $this->download($url);
153
        
154 1
        if ($data) {
155
            call_user_func($this->cache_handler, 'set', $url, $data);
156
        }
157
158 1
        return $data;
159
    }
160
161
    // arrangeParams converts a parameters array to a query string.
162
    // In addition, we add some patches:
163
    //  1) trim_start and trim_end are converted from any plain
164
    //     language syntax to Quandl format
165
    //  2) api_key is appended
166 8
    private function arrangeParams($params)
167
    {
168 8
        if ($this->api_key) {
169 8
            $params['auth_token'] = $this->api_key;
170 8
        }
171
        
172 8
        if (!$params) {
173
            return $params;   
174
        }
175
        
176 8
        foreach (['trim_start', 'trim_end'] as $v) {
177 8
            if (isset($params[$v])) {
178 8
                $params[$v] = self::convertToQuandlDate($params[$v]);
179 8
            }
180 8
        }
181
        
182 8
        return http_build_query($params);
183
    }
184
185
    // convertToQuandlDate converts any time string supported by
186
    // PHP (e.g. "today-30 days") to the format needed by Quandl
187 8
    private static function convertToQuandlDate($time_str)
188
    {
189 8
        return date('Y-m-d', strtotime($time_str));
190
    }
191
192
    /*
193
     * download fetches url with file_get_contents or curl fallback
194
     * You can force curl download by setting force_curl to true.
195
     * You can disable SSL verification for curl by setting 
196
     * no_ssl_verify to true (solves "SSL certificate problem")
197
     */
198 8
    private function download($url)
199
    {
200 8
        if (! $this->checkUrl($url)) {
201
            return false;
202
        }
203
        
204 8
        if (ini_get('allow_url_fopen') && !$this->force_curl) {
205 8
            return $this->getDataWithFileGetContents($url);
206
        }
207
208
        if (!function_exists('curl_version')) {
209
            $this->error = 'Enable allow_url_fopen or curl';
210
            return false;
211
        }
212
213
        return $this->getDataWithCurl($url);
214
    }
215
216
    //check url
217 8
    private function checkUrl($url)
218
    {
219 8
        $headers_url = get_headers($url);
220 8
        $http_code   = $headers_url[0];
221
222 8
        if (strpos($http_code, '404') !== false) {
223
            $this->error = 'URL not found or invalid URL';
224
            return false;
225
        }
226
227 8
        return true;
228
    }
229
230
    //download data with file_get_contents PHP function
231 8
    private function getDataWithFileGetContents($url)
232
    {
233
        try {
234 8
            return file_get_contents($url);
235 8
        } catch (\Exception $e) {
236 8
            $this->error = $e->getMessage();
237
        }
238 8
    }
239
240
    //download data with file_get_contents PHP function
241
    private function getDataWithCurl($url)
242
    {
243
        $curl = curl_init();
244
            
245
        curl_setopt($curl, CURLOPT_URL, $url);
246
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
247
248
        // disable ssl verification for curl
249
        if ($this->no_ssl_verify) {
250
            curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
251
        }
252
           
253
        $data  = curl_exec($curl);
254
        $error = curl_error($curl);
255
256
        curl_close($curl);
257
258
        if (! $error) {
259
            return $data;
260
        }
261
        
262
        $this->error = $error;
263
        
264
        return false;
265
    }
266
267
    //construct a array with parameters used to query
268
    private function constructParams($query, $page = 1, $per_page = 300)
269
    {
270
        $params = [
271
            "per_page" => $per_page, 
272
            "page"     => $page, 
273
            "query"    => $query,
274
        ];
275
276
        return $params;
277
    }
278
}
279