Completed
Pull Request — master (#97)
by lee
02:09
created

OpenWeatherMap::getUviHistory()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 4

Duplication

Lines 7
Ratio 100 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
dl 7
loc 7
ccs 0
cts 5
cp 0
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 4
nc 1
nop 2
crap 2
1
<?php
2
/**
3
 * OpenWeatherMap-PHP-API — A php api to parse weather data from http://www.OpenWeatherMap.org .
4
 *
5
 * @license MIT
6
 *
7
 * Please see the LICENSE file distributed with this source code for further
8
 * information regarding copyright and licensing.
9
 *
10
 * Please visit the following links to read about the usage policies and the license of
11
 * OpenWeatherMap before using this class:
12
 *
13
 * @see http://www.OpenWeatherMap.org
14
 * @see http://www.OpenWeatherMap.org/terms
15
 * @see http://openweathermap.org/appid
16
 */
17
18
namespace Cmfcmf;
19
20
use Cmfcmf\OpenWeatherMap\AbstractCache;
21
use Cmfcmf\OpenWeatherMap\CurrentWeather;
22
use Cmfcmf\OpenWeatherMap\CurrentUvi;
23
use Cmfcmf\OpenWeatherMap\CurrentWeatherGroup;
24
use Cmfcmf\OpenWeatherMap\Exception as OWMException;
25
use Cmfcmf\OpenWeatherMap\Fetcher\CurlFetcher;
26
use Cmfcmf\OpenWeatherMap\Fetcher\FetcherInterface;
27
use Cmfcmf\OpenWeatherMap\Fetcher\FileGetContentsFetcher;
28
use Cmfcmf\OpenWeatherMap\WeatherForecast;
29
use Cmfcmf\OpenWeatherMap\WeatherHistory;
30
31
/**
32
 * Main class for the OpenWeatherMap-PHP-API. Only use this class.
33
 *
34
 * @api
35
 */
36
class OpenWeatherMap
37
{
38
    /**
39
     * The copyright notice. This is no official text, it was created by
40
     * following the guidelines at http://openweathermap.org/copyright.
41
     *
42
     * @var string $copyright
43
     */
44
    const COPYRIGHT = "Weather data from <a href=\"http://openweathermap.org\">OpenWeatherMap.org</a>";
45
46
    /**
47
     * @var string The basic api url to fetch weather data from.
48
     */
49
    private $weatherUrl = 'http://api.openweathermap.org/data/2.5/weather?';
50
51
    /**
52
     * @var string The basic api url to fetch weather group data from.
53
     */
54
    private $weatherGroupUrl = 'http://api.openweathermap.org/data/2.5/group?';
55
56
    /**
57
     * @var string The basic api url to fetch weekly forecast data from.
58
     */
59
    private $weatherHourlyForecastUrl = 'http://api.openweathermap.org/data/2.5/forecast?';
60
61
    /**
62
     * @var string The basic api url to fetch daily forecast data from.
63
     */
64
    private $weatherDailyForecastUrl = 'http://api.openweathermap.org/data/2.5/forecast/daily?';
65
66
    /**
67
     * @var string The basic api url to fetch history weather data from.
68
     */
69
    private $weatherHistoryUrl = 'http://history.openweathermap.org/data/2.5/history/city?';
70
71
    /**
72
     * @var string The basic api url to fetch current uv data from.
73
     */
74
    private $uviUrl = 'http://api.openweathermap.org/v3/uvi/%s,%s/current.json?';
75
76
    /**
77
     * @var string The basic api url to fetch current uv data from.
78
     */
79
    private $uviHistoryUrl = 'http://api.openweathermap.org/v3/uvi/%s,%s/%s.json?';
80
81
    /**
82
     * @var AbstractCache|bool $cache The cache to use.
83
     */
84
    private $cache = false;
85
86
    /**
87
     * @var int
88
     */
89
    private $seconds;
90
91
    /**
92
     * @var bool
93
     */
94
    private $wasCached = false;
95
96
    /**
97
     * @var FetcherInterface The url fetcher.
98
     */
99
    private $fetcher;
100
101
    /**
102
     * @var string
103
     */
104
    private $apiKey = '';
105
106
    /**
107
     * Constructs the OpenWeatherMap object.
108
     *
109
     * @param string                $apiKey  The OpenWeatherMap API key. Required and only optional for BC.
110
     * @param null|FetcherInterface $fetcher The interface to fetch the data from OpenWeatherMap. Defaults to
111
     *                                       CurlFetcher() if cURL is available. Otherwise defaults to
112
     *                                       FileGetContentsFetcher() using 'file_get_contents()'.
113
     * @param bool|string           $cache   If set to false, caching is disabled. Otherwise this must be a class
114
     *                                       extending AbstractCache. Defaults to false.
115
     * @param int $seconds                   How long weather data shall be cached. Default 10 minutes.
116
     *
117
     * @throws \Exception If $cache is neither false nor a valid callable extending Cmfcmf\OpenWeatherMap\Util\Cache.
118
     *
119
     * @api
120
     */
121
    public function __construct($apiKey = '', $fetcher = null, $cache = false, $seconds = 600)
122
    {
123
        if (!is_string($apiKey) || empty($apiKey)) {
124
            // BC
125
            $seconds = $cache !== false ? $cache : 600;
126
            $cache = $fetcher !== null ? $fetcher : false;
127
            $fetcher = $apiKey !== '' ? $apiKey : null;
128
        } else {
129
            $this->apiKey = $apiKey;
130
        }
131
132
        if ($cache !== false && !($cache instanceof AbstractCache)) {
133
            throw new \InvalidArgumentException('The cache class must implement the FetcherInterface!');
134
        }
135
        if (!is_numeric($seconds)) {
136
            throw new \InvalidArgumentException('$seconds must be numeric.');
137
        }
138
        if (!isset($fetcher)) {
139
            $fetcher = (function_exists('curl_version')) ? new CurlFetcher() : new FileGetContentsFetcher();
140
        }
141
        if ($seconds == 0) {
142
            $cache = false;
143
        }
144
145
        $this->cache = $cache;
146
        $this->seconds = $seconds;
0 ignored issues
show
Documentation Bug introduced by
It seems like $seconds can also be of type double or string. However, the property $seconds is declared as type integer. 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...
147
        $this->fetcher = $fetcher;
0 ignored issues
show
Documentation Bug introduced by
It seems like $fetcher can also be of type string. However, the property $fetcher is declared as type object<Cmfcmf\OpenWeathe...tcher\FetcherInterface>. 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...
148
    }
149
150
    /**
151
     * Sets the API Key.
152
     *
153
     * @param string $apiKey API key for the OpenWeatherMap account.
154
     *
155
     * @api
156
     */
157
    public function setApiKey($apiKey)
158
    {
159
        $this->apiKey = $apiKey;
160
    }
161
162
    /**
163
     * Returns the API Key.
164
     *
165
     * @return string
166
     *
167
     * @api
168
     */
169
    public function getApiKey()
170
    {
171
        return $this->apiKey;
172
    }
173
174
    /**
175
     * Returns the current weather at the place you specified.
176
     *
177
     * @param array|int|string $query The place to get weather information for. For possible values see below.
178
     * @param string           $units Can be either 'metric' or 'imperial' (default). This affects almost all units returned.
179
     * @param string           $lang  The language to use for descriptions, default is 'en'. For possible values see http://openweathermap.org/current#multi.
180
     * @param string           $appid Your app id, default ''. See http://openweathermap.org/appid for more details.
181
     *
182
     * @throws OpenWeatherMap\Exception  If OpenWeatherMap returns an error.
183
     * @throws \InvalidArgumentException If an argument error occurs.
184
     *
185
     * @return CurrentWeather The weather object.
186
     *
187
     * There are three ways to specify the place to get weather information for:
188
     * - Use the city name: $query must be a string containing the city name.
189
     * - Use the city id: $query must be an integer containing the city id.
190
     * - Use the coordinates: $query must be an associative array containing the 'lat' and 'lon' values.
191
     *
192
     * @api
193
     */
194
    public function getWeather($query, $units = 'imperial', $lang = 'en', $appid = '')
195
    {
196
        $answer = $this->getRawWeatherData($query, $units, $lang, $appid, 'xml');
197
        $xml = $this->parseXML($answer);
0 ignored issues
show
Bug introduced by
It seems like $answer defined by $this->getRawWeatherData..., $lang, $appid, 'xml') on line 196 can also be of type boolean; however, Cmfcmf\OpenWeatherMap::parseXML() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
198
199
        return new CurrentWeather($xml, $units);
200
    }
201
202
    /**
203
     * Returns the current weather for a group of city ids.
204
     *
205
     * @param array  $ids   The city ids to get weather information for
206
     * @param string $units Can be either 'metric' or 'imperial' (default). This affects almost all units returned.
207
     * @param string $lang  The language to use for descriptions, default is 'en'. For possible values see http://openweathermap.org/current#multi.
208
     * @param string $appid Your app id, default ''. See http://openweathermap.org/appid for more details.
209
     *
210
     * @throws OpenWeatherMap\Exception  If OpenWeatherMap returns an error.
211
     * @throws \InvalidArgumentException If an argument error occurs.
212
     *
213
     * @return CurrentWeatherGroup
214
     *
215
     * @api
216
     */
217
    public function getWeatherGroup($ids, $units = 'imperial', $lang = 'en', $appid = '')
218
    {
219
        $answer = $this->getRawWeatherGroupData($ids, $units, $lang, $appid);
220
        $json = $this->parseJson($answer);
0 ignored issues
show
Bug introduced by
It seems like $answer defined by $this->getRawWeatherGrou... $units, $lang, $appid) on line 219 can also be of type boolean; however, Cmfcmf\OpenWeatherMap::parseJson() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
221
222
        return new CurrentWeatherGroup($json, $units);
223
    }
224
225
    /**
226
     * Returns the forecast for the place you specified. DANGER: Might return
227
     * fewer results than requested due to a bug in the OpenWeatherMap API!
228
     *
229
     * @param array|int|string $query The place to get weather information for. For possible values see ::getWeather.
230
     * @param string           $units Can be either 'metric' or 'imperial' (default). This affects almost all units returned.
231
     * @param string           $lang  The language to use for descriptions, default is 'en'. For possible values see http://openweathermap.org/current#multi.
232
     * @param string           $appid Your app id, default ''. See http://openweathermap.org/appid for more details.
233
     * @param int              $days  For how much days you want to get a forecast. Default 1, maximum: 16.
234
     *
235
     * @throws OpenWeatherMap\Exception If OpenWeatherMap returns an error.
236
     * @throws \InvalidArgumentException If an argument error occurs.
237
     *
238
     * @return WeatherForecast
239
     *
240
     * @api
241
     */
242
    public function getWeatherForecast($query, $units = 'imperial', $lang = 'en', $appid = '', $days = 1)
243
    {
244
        if ($days <= 5) {
245
            $answer = $this->getRawHourlyForecastData($query, $units, $lang, $appid, 'xml');
246
        } elseif ($days <= 16) {
247
            $answer = $this->getRawDailyForecastData($query, $units, $lang, $appid, 'xml', $days);
248
        } else {
249
            throw new \InvalidArgumentException('Error: forecasts are only available for the next 16 days. $days must be 16 or lower.');
250
        }
251
        $xml = $this->parseXML($answer);
0 ignored issues
show
Bug introduced by
It seems like $answer can also be of type boolean; however, Cmfcmf\OpenWeatherMap::parseXML() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
252
253
        return new WeatherForecast($xml, $units, $days);
254
    }
255
256
    /**
257
     * Returns the DAILY forecast for the place you specified. DANGER: Might return
258
     * fewer results than requested due to a bug in the OpenWeatherMap API!
259
     *
260
     * @param array|int|string $query The place to get weather information for. For possible values see ::getWeather.
261
     * @param string           $units Can be either 'metric' or 'imperial' (default). This affects almost all units returned.
262
     * @param string           $lang  The language to use for descriptions, default is 'en'. For possible values see http://openweathermap.org/current#multi.
263
     * @param string           $appid Your app id, default ''. See http://openweathermap.org/appid for more details.
264
     * @param int              $days  For how much days you want to get a forecast. Default 1, maximum: 16.
265
     *
266
     * @throws OpenWeatherMap\Exception If OpenWeatherMap returns an error.
267
     * @throws \InvalidArgumentException If an argument error occurs.
268
     *
269
     * @return WeatherForecast
270
     *
271
     * @api
272
     */
273
    public function getDailyWeatherForecast($query, $units = 'imperial', $lang = 'en', $appid = '', $days = 1)
274
    {
275
        if ($days > 16) {
276
            throw new \InvalidArgumentException('Error: forecasts are only available for the next 16 days. $days must be 16 or lower.');
277
        }
278
279
        $answer = $this->getRawDailyForecastData($query, $units, $lang, $appid, 'xml', $days);
280
        $xml = $this->parseXML($answer);
0 ignored issues
show
Bug introduced by
It seems like $answer defined by $this->getRawDailyForeca..., $appid, 'xml', $days) on line 279 can also be of type boolean; however, Cmfcmf\OpenWeatherMap::parseXML() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
281
        return new WeatherForecast($xml, $units, $days);
282
    }
283
284
    /**
285
     * Returns the weather history for the place you specified.
286
     *
287
     * @param array|int|string $query      The place to get weather information for. For possible values see ::getWeather.
288
     * @param \DateTime        $start
289
     * @param int              $endOrCount
290
     * @param string           $type       Can either be 'tick', 'hour' or 'day'.
291
     * @param string           $units      Can be either 'metric' or 'imperial' (default). This affects almost all units returned.
292
     * @param string           $lang       The language to use for descriptions, default is 'en'. For possible values see http://openweathermap.org/current#multi.
293
     * @param string           $appid      Your app id, default ''. See http://openweathermap.org/appid for more details.
294
     *
295
     * @throws OpenWeatherMap\Exception  If OpenWeatherMap returns an error.
296
     * @throws \InvalidArgumentException If an argument error occurs.
297
     *
298
     * @return WeatherHistory
299
     *
300
     * @api
301
     */
302
    public function getWeatherHistory($query, \DateTime $start, $endOrCount = 1, $type = 'hour', $units = 'imperial', $lang = 'en', $appid = '')
303
    {
304 View Code Duplication
        if (!in_array($type, array('tick', 'hour', 'day'))) {
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...
305
            throw new \InvalidArgumentException('$type must be either "tick", "hour" or "day"');
306
        }
307
308
        $xml = json_decode($this->getRawWeatherHistory($query, $start, $endOrCount, $type, $units, $lang, $appid), true);
309
310
        if ($xml['cod'] != 200) {
311
            throw new OWMException($xml['message'], $xml['cod']);
312
        }
313
314
        return new WeatherHistory($xml, $query);
315
    }
316
317
    /**
318
     * Directly returns the xml/json/html string returned by OpenWeatherMap for the current weather.
319
     *
320
     * @param array|int|string $query The place to get weather information for. For possible values see ::getWeather.
321
     * @param string           $units Can be either 'metric' or 'imperial' (default). This affects almost all units returned.
322
     * @param string           $lang  The language to use for descriptions, default is 'en'. For possible values see http://openweathermap.org/current#multi.
323
     * @param string           $appid Your app id, default ''. See http://openweathermap.org/appid for more details.
324
     * @param string           $mode  The format of the data fetched. Possible values are 'json', 'html' and 'xml' (default).
325
     *
326
     * @return string Returns false on failure and the fetched data in the format you specified on success.
327
     *
328
     * Warning: If an error occurs, OpenWeatherMap ALWAYS returns json data.
329
     *
330
     * @api
331
     */
332
    public function getRawWeatherData($query, $units = 'imperial', $lang = 'en', $appid = '', $mode = 'xml')
333
    {
334
        $url = $this->buildUrl($query, $units, $lang, $appid, $mode, $this->weatherUrl);
335
336
        return $this->cacheOrFetchResult($url);
0 ignored issues
show
Bug introduced by
It seems like $url defined by $this->buildUrl($query, ...ode, $this->weatherUrl) on line 334 can also be of type boolean; however, Cmfcmf\OpenWeatherMap::cacheOrFetchResult() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
337
    }
338
339
    /**
340
     * Directly returns the JSON string returned by OpenWeatherMap for the group of current weather.
341
     * Only a JSON response format is supported for this webservice.
342
     *
343
     * @param array  $ids   The city ids to get weather information for
344
     * @param string $units Can be either 'metric' or 'imperial' (default). This affects almost all units returned.
345
     * @param string $lang  The language to use for descriptions, default is 'en'. For possible values see http://openweathermap.org/current#multi.
346
     * @param string $appid Your app id, default ''. See http://openweathermap.org/appid for more details.
347
     *
348
     * @return string Returns false on failure and the fetched data in the format you specified on success.
349
     *
350
     * @api
351
     */
352
    public function getRawWeatherGroupData($ids, $units = 'imperial', $lang = 'en', $appid = '')
353
    {
354
        $url = $this->buildUrl($ids, $units, $lang, $appid, 'json', $this->weatherGroupUrl);
355
356
        return $this->cacheOrFetchResult($url);
0 ignored issues
show
Bug introduced by
It seems like $url defined by $this->buildUrl($ids, $u...$this->weatherGroupUrl) on line 354 can also be of type boolean; however, Cmfcmf\OpenWeatherMap::cacheOrFetchResult() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
357
    }
358
359
    /**
360
     * Directly returns the xml/json/html string returned by OpenWeatherMap for the hourly forecast.
361
     *
362
     * @param array|int|string $query The place to get weather information for. For possible values see ::getWeather.
363
     * @param string           $units Can be either 'metric' or 'imperial' (default). This affects almost all units returned.
364
     * @param string           $lang  The language to use for descriptions, default is 'en'. For possible values see http://openweathermap.org/current#multi.
365
     * @param string           $appid Your app id, default ''. See http://openweathermap.org/appid for more details.
366
     * @param string           $mode  The format of the data fetched. Possible values are 'json', 'html' and 'xml' (default).
367
     *
368
     * @return string Returns false on failure and the fetched data in the format you specified on success.
369
     *
370
     * Warning: If an error occurs, OpenWeatherMap ALWAYS returns json data.
371
     *
372
     * @api
373
     */
374
    public function getRawHourlyForecastData($query, $units = 'imperial', $lang = 'en', $appid = '', $mode = 'xml')
375
    {
376
        $url = $this->buildUrl($query, $units, $lang, $appid, $mode, $this->weatherHourlyForecastUrl);
377
378
        return $this->cacheOrFetchResult($url);
0 ignored issues
show
Bug introduced by
It seems like $url defined by $this->buildUrl($query, ...atherHourlyForecastUrl) on line 376 can also be of type boolean; however, Cmfcmf\OpenWeatherMap::cacheOrFetchResult() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
379
    }
380
381
    /**
382
     * Directly returns the xml/json/html string returned by OpenWeatherMap for the daily forecast.
383
     *
384
     * @param array|int|string $query The place to get weather information for. For possible values see ::getWeather.
385
     * @param string           $units Can be either 'metric' or 'imperial' (default). This affects almost all units returned.
386
     * @param string           $lang  The language to use for descriptions, default is 'en'. For possible values see http://openweathermap.org/current#multi.
387
     * @param string           $appid Your app id, default ''. See http://openweathermap.org/appid for more details.
388
     * @param string           $mode  The format of the data fetched. Possible values are 'json', 'html' and 'xml' (default)
389
     * @param int              $cnt   How many days of forecast shall be returned? Maximum (and default): 16
390
     *
391
     * @throws \InvalidArgumentException If $cnt is higher than 16.
392
     *
393
     * @return string Returns false on failure and the fetched data in the format you specified on success.
394
     *
395
     * Warning: If an error occurs, OpenWeatherMap ALWAYS returns json data.
396
     *
397
     * @api
398
     */
399
    public function getRawDailyForecastData($query, $units = 'imperial', $lang = 'en', $appid = '', $mode = 'xml', $cnt = 16)
400
    {
401
        if ($cnt > 16) {
402
            throw new \InvalidArgumentException('$cnt must be 16 or lower!');
403
        }
404
        $url = $this->buildUrl($query, $units, $lang, $appid, $mode, $this->weatherDailyForecastUrl) . "&cnt=$cnt";
405
406
        return $this->cacheOrFetchResult($url);
407
    }
408
409
    /**
410
     * Directly returns the xml/json/html string returned by OpenWeatherMap for the weather history.
411
     *
412
     * @param array|int|string $query      The place to get weather information for. For possible values see ::getWeather.
413
     * @param \DateTime        $start      The \DateTime object of the date to get the first weather information from.
414
     * @param \DateTime|int    $endOrCount Can be either a \DateTime object representing the end of the period to
415
     *                                     receive weather history data for or an integer counting the number of
416
     *                                     reports requested.
417
     * @param string           $type       The period of the weather history requested. Can be either be either "tick",
418
     *                                     "hour" or "day".
419
     * @param string           $units      Can be either 'metric' or 'imperial' (default). This affects almost all units returned.
420
     * @param string           $lang       The language to use for descriptions, default is 'en'. For possible values see http://openweathermap.org/current#multi.
421
     * @param string           $appid      Your app id, default ''. See http://openweathermap.org/appid for more details.
422
     *
423
     * @throws \InvalidArgumentException
424
     *
425
     * @return string Returns false on failure and the fetched data in the format you specified on success.
426
     *
427
     * Warning If an error occurred, OpenWeatherMap ALWAYS returns data in json format.
428
     *
429
     * @api
430
     */
431
    public function getRawWeatherHistory($query, \DateTime $start, $endOrCount = 1, $type = 'hour', $units = 'imperial', $lang = 'en', $appid = '')
432
    {
433 View Code Duplication
        if (!in_array($type, array('tick', 'hour', 'day'))) {
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...
434
            throw new \InvalidArgumentException('$type must be either "tick", "hour" or "day"');
435
        }
436
437
        $url = $this->buildUrl($query, $units, $lang, $appid, 'json', $this->weatherHistoryUrl);
438
        $url .= "&type=$type&start={$start->format('U')}";
439
        if ($endOrCount instanceof \DateTime) {
440
            $url .= "&end={$endOrCount->format('U')}";
441
        } elseif (is_numeric($endOrCount) && $endOrCount > 0) {
442
            $url .= "&cnt=$endOrCount";
443
        } else {
444
            throw new \InvalidArgumentException('$endOrCount must be either a \DateTime or a positive integer.');
445
        }
446
447
        return $this->cacheOrFetchResult($url);
448
    }
449
450
    /**
451
     * Directly returns the json string returned by OpenWeatherMap for the UVI data.
452
     *
453
     * @param array $query     The place to get information as follows: [latitude, longitude, date time]. For possible values see ::getWeather.
454
     * @param string           $appid      Your app id, default ''. See http://openweathermap.org/appid for more details.
455
     *
456
     * @throws \InvalidArgumentException
457
     *
458
     * @return string Returns false on failure and the fetched data in the format you specified on success.
459
     *
460
     * Warning If an error occurred, OpenWeatherMap ALWAYS returns data in json format.
461
     *
462
     * @api
463
     */
464 View Code Duplication
    public function getRawUviData($query, $appid = '')
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
465
    {
466
        if (!is_array($query)) {
467
            throw new \InvalidArgumentException('$query must get information is as follows: [latitude, longitude]');
468
        } elseif(count($query) != 2) {
469
            throw new \InvalidArgumentException('$query must get information is as follows: [latitude, longitude]');
470
        } else {
471
            $url = $this->buildUviUrl($query, $appid);
472
        }
473
474
        return $this->cacheOrFetchResult($url);
0 ignored issues
show
Bug introduced by
It seems like $url defined by $this->buildUviUrl($query, $appid) on line 471 can also be of type boolean; however, Cmfcmf\OpenWeatherMap::cacheOrFetchResult() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
475
    }
476
477
    /**
478
     * Directly returns the json string returned by OpenWeatherMap for the UVI history data.
479
     *
480
     * @param array|int|string $query      The place to get weather information for. For possible values see ::getWeather.
481
     * @param string           $appid      Your app id, default ''. See http://openweathermap.org/appid for more details.
482
     *
483
     * @throws \InvalidArgumentException
484
     *
485
     * @return string Returns false on failure and the fetched data in the format you specified on success.
486
     *
487
     * Warning If an error occurred, OpenWeatherMap ALWAYS returns data in json format.
488
     *
489
     * @api
490
     */
491 View Code Duplication
    public function getRawUviHistory($query, $appid = '')
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
492
    {
493
        if (!is_array($query)) {
494
            throw new \InvalidArgumentException('$query must get information is as follows: [latitude, longitude, ISO 8601 date format]');
495
        } elseif(count($query) != 3) {
496
            throw new \InvalidArgumentException('$query must get information is as follows: [latitude, longitude, ISO 8601 date format]');
497
        } else {
498
            $url = $this->buildUviUrl($query, $appid);
499
        }
500
501
        return $this->cacheOrFetchResult($url);
0 ignored issues
show
Bug introduced by
It seems like $url defined by $this->buildUviUrl($query, $appid) on line 498 can also be of type boolean; however, Cmfcmf\OpenWeatherMap::cacheOrFetchResult() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
502
    }
503
504
     /**
505
     * Returns the current uvi at the location you specified.
506
     *
507
     * @param array|int|string $query The place to get weather information for. For possible values see below.
508
     * @param string           $appid Your app id, default ''. See http://openweathermap.org/appid for more details.
509
     *
510
     * @throws OpenWeatherMap\Exception  If OpenWeatherMap returns an error.
511
     * @throws \InvalidArgumentException If an argument error occurs.
512
     *
513
     * @return CurrentUvi The uvi object.
514
     *
515
     * There are three ways to specify the place to get weather information for:
516
     * - Use the coordinates: $query must be an associative array containing the 'lat' and 'lon' values.
517
     *
518
     * @api
519
     */
520 View Code Duplication
    public function getUvi($query, $appid = '')
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
521
    {
522
        $answer = $this->getRawUviData($query, $appid);
523
        $json = $this->parseJson($answer);
0 ignored issues
show
Bug introduced by
It seems like $answer defined by $this->getRawUviData($query, $appid) on line 522 can also be of type boolean; however, Cmfcmf\OpenWeatherMap::parseJson() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
524
525
        return new CurrentUvi($json);
526
    }
527
528
    /**
529
     * Returns the history uvi at the location you specified.
530
     *
531
     * @param array|int|string $query The place to get weather information for. For possible values see below.
532
     * @param string           $appid Your app id, default ''. See http://openweathermap.org/appid for more details.
533
     * @param string           $dateTime Your date time, default ''. See http://openweathermap.org/api/uvi for more details about date format.
0 ignored issues
show
Bug introduced by
There is no parameter named $dateTime. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
534
     *
535
     * @throws OpenWeatherMap\Exception  If OpenWeatherMap returns an error.
536
     * @throws \InvalidArgumentException If an argument error occurs.
537
     *
538
     * @return CurrentUvi The uvi object.
539
     *
540
     * There are three ways to specify the place to get weather information for:
541
     * - Use the coordinates: $query must be an associative array containing the 'lat' and 'lon' values.
542
     *
543
     * @api
544
     */
545 View Code Duplication
    public function getUviHistory($query, $appid = '')
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
546
    {
547
        $answer = $this->getRawUviHistory($query, $appid);
548
        $json = $this->parseJson($answer);
0 ignored issues
show
Bug introduced by
It seems like $answer defined by $this->getRawUviHistory($query, $appid) on line 547 can also be of type boolean; however, Cmfcmf\OpenWeatherMap::parseJson() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
549
550
        return new CurrentUvi($json);
551
    }
552
553
    /**
554
     * Returns whether or not the last result was fetched from the cache.
555
     *
556
     * @return bool true if last result was fetched from cache, false otherwise.
557
     */
558
    public function wasCached()
559
    {
560
        return $this->wasCached;
561
    }
562
563
    /**
564
     * @deprecated Use {@link self::getRawWeatherData()} instead.
565
     */
566
    public function getRawData($query, $units = 'imperial', $lang = 'en', $appid = '', $mode = 'xml')
567
    {
568
        return $this->getRawWeatherData($query, $units, $lang, $appid, $mode);
569
    }
570
571
    /**
572
     * Fetches the result or delivers a cached version of the result.
573
     *
574
     * @param string $url
575
     *
576
     * @return string
577
     */
578
    private function cacheOrFetchResult($url)
579
    {
580
        if ($this->cache !== false) {
581
            /** @var AbstractCache $cache */
582
            $cache = $this->cache;
583
            $cache->setSeconds($this->seconds);
584
            if ($cache->isCached($url)) {
585
                $this->wasCached = true;
586
                return $cache->getCached($url);
587
            }
588
            $result = $this->fetcher->fetch($url);
589
            $cache->setCached($url, $result);
590
        } else {
591
            $result = $this->fetcher->fetch($url);
592
        }
593
        $this->wasCached = false;
594
595
        return $result;
596
    }
597
598
    /**
599
     * Build the url to fetch weather data from.
600
     *
601
     * @param        $query
602
     * @param        $units
603
     * @param        $lang
604
     * @param        $appid
605
     * @param        $mode
606
     * @param string $url   The url to prepend.
607
     *
608
     * @return bool|string The fetched url, false on failure.
609
     */
610
    private function buildUrl($query, $units, $lang, $appid, $mode, $url)
611
    {
612
        $queryUrl = $this->buildQueryUrlParameter($query);
613
614
        $url = $url."$queryUrl&units=$units&lang=$lang&mode=$mode&APPID=";
615
        $url .= empty($appid) ? $this->apiKey : $appid;
616
617
        return $url;
618
    }
619
620
    /**
621
     * Build the url to fetch UVI data from.
622
     *
623
     * @param        $query
624
     * @param        $units
625
     * @param        $lang
626
     * @param        $appid
627
     * @param        $mode
628
     * @param string $url   The url to prepend.
0 ignored issues
show
Bug introduced by
There is no parameter named $url. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
629
     *
630
     * @return bool|string The fetched url, false on failure.
631
     */
632
    private function buildUviUrl($query, $appid)
633
    {
634
        $queryLength = count($query);
635
        switch ($queryLength) {
636
            case 2:
637
                $queryUrl = sprintf($this->uviUrl, $query[0], $query[1]);
638
                break;
639
            case 3:
640
                $queryUrl = sprintf($this->uviHistoryUrl, $query[0], $query[1], $query[2]);
641
                break;
642
        }
643
        $queryUrl .= 'APPID=';
0 ignored issues
show
Bug introduced by
The variable $queryUrl does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
644
645
        return $queryUrl .= empty($appid) ? $this->apiKey : $appid;
646
    }
647
648
    /**
649
     * Builds the query string for the url.
650
     *
651
     * @param mixed $query
652
     *
653
     * @return string The built query string for the url.
654
     *
655
     * @throws \InvalidArgumentException If the query parameter is invalid.
656
     */
657
    private function buildQueryUrlParameter($query)
658
    {
659
        switch ($query) {
660
            case is_array($query) && isset($query['lat']) && isset($query['lon']) && is_numeric($query['lat']) && is_numeric($query['lon']):
661
                return "lat={$query['lat']}&lon={$query['lon']}";
662
            case is_array($query) && is_numeric($query[0]):
663
                return 'id='.implode(',', $query);
664
            case is_numeric($query):
665
                return "id=$query";
666
            case is_string($query):
667
                return 'q='.urlencode($query);
668
            default:
669
                throw new \InvalidArgumentException('Error: $query has the wrong format. See the documentation of OpenWeatherMap::getWeather() to read about valid formats.');
670
        }
671
    }
672
673
    /**
674
     * @param string $answer The content returned by OpenWeatherMap.
675
     *
676
     * @return \SimpleXMLElement
677
     * @throws OWMException If the content isn't valid XML.
678
     */
679
    private function parseXML($answer)
680
    {
681
        // Disable default error handling of SimpleXML (Do not throw E_WARNINGs).
682
        libxml_use_internal_errors(true);
683
        libxml_clear_errors();
684
        try {
685
            return new \SimpleXMLElement($answer);
686
        } catch (\Exception $e) {
687
            // Invalid xml format. This happens in case OpenWeatherMap returns an error.
688
            // OpenWeatherMap always uses json for errors, even if one specifies xml as format.
689
            $error = json_decode($answer, true);
690
            if (isset($error['message'])) {
691
                throw new OWMException($error['message'], isset($error['cod']) ? $error['cod'] : 0);
692
            } else {
693
                throw new OWMException('Unknown fatal error: OpenWeatherMap returned the following json object: ' . $answer);
694
            }
695
        }
696
    }
697
698
    /**
699
     * @param string $answer The content returned by OpenWeatherMap.
700
     *
701
     * @return \stdClass
702
     * @throws OWMException If the content isn't valid JSON.
703
     */
704
    private function parseJson($answer)
705
    {
706
        $json = json_decode($answer);
707
        if (json_last_error() !== JSON_ERROR_NONE) {
708
            throw new OWMException('OpenWeatherMap returned an invalid json object. JSON error was: ' . $this->json_last_error_msg());
709
        }
710
711
        return $json;
712
    }
713
714
    private function json_last_error_msg()
715
    {
716
        if (function_exists('json_last_error_msg')) {
717
            return json_last_error_msg();
718
        }
719
720
        static $ERRORS = array(
721
            JSON_ERROR_NONE => 'No error',
722
            JSON_ERROR_DEPTH => 'Maximum stack depth exceeded',
723
            JSON_ERROR_STATE_MISMATCH => 'State mismatch (invalid or malformed JSON)',
724
            JSON_ERROR_CTRL_CHAR => 'Control character error, possibly incorrectly encoded',
725
            JSON_ERROR_SYNTAX => 'Syntax error',
726
            JSON_ERROR_UTF8 => 'Malformed UTF-8 characters, possibly incorrectly encoded'
727
        );
728
729
        $error = json_last_error();
730
        return isset($ERRORS[$error]) ? $ERRORS[$error] : 'Unknown error';
731
    }
732
}
733