]> git.mxchange.org Git - friendica-addons.git/blob - curweather/vendor/cmfcmf/openweathermap-php-api/Cmfcmf/OpenWeatherMap.php
added composer.json and needed libs
[friendica-addons.git] / curweather / vendor / cmfcmf / openweathermap-php-api / Cmfcmf / OpenWeatherMap.php
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\Exception as OWMException;
23 use Cmfcmf\OpenWeatherMap\Fetcher\CurlFetcher;
24 use Cmfcmf\OpenWeatherMap\Fetcher\FetcherInterface;
25 use Cmfcmf\OpenWeatherMap\Fetcher\FileGetContentsFetcher;
26 use Cmfcmf\OpenWeatherMap\WeatherForecast;
27 use Cmfcmf\OpenWeatherMap\WeatherHistory;
28
29 /**
30  * Main class for the OpenWeatherMap-PHP-API. Only use this class.
31  *
32  * @api
33  */
34 class OpenWeatherMap
35 {
36     /**
37      * @var string $weatherUrl The basic api url to fetch weather data from.
38      */
39     private $weatherUrl = "http://api.openweathermap.org/data/2.5/weather?";
40
41     /**
42      * @var string $url The basic api url to fetch weekly forecast data from.
43      */
44     private $weatherHourlyForecastUrl = "http://api.openweathermap.org/data/2.5/forecast?";
45
46     /**
47      * @var string $url The basic api url to fetch daily forecast data from.
48      */
49     private $weatherDailyForecastUrl = "http://api.openweathermap.org/data/2.5/forecast/daily?";
50
51     /**
52      * @var string $url The basic api url to fetch history weather data from.
53      */
54     private $weatherHistoryUrl = "http://api.openweathermap.org/data/2.5/history/city?";
55
56     /**
57      * The copyright notice. This is no official text, this hint was created regarding to http://openweathermap.org/copyright.
58      *
59      * @var string $copyright
60      */
61     const COPYRIGHT = "Weather data from <a href=\"http://www.openweathermap.org\">OpenWeatherMap.org</a>";
62
63     /**
64      * @var \Cmfcmf\OpenWeatherMap\AbstractCache|bool $cacheClass The cache class.
65      */
66     private $cacheClass = false;
67
68     /**
69      * @var int
70      */
71     private $seconds;
72
73     /**
74      * @var FetcherInterface The url fetcher.
75      */
76     private $fetcher;
77
78     /**
79      * Constructs the OpenWeatherMap object.
80      *
81      * @param null|FetcherInterface $fetcher    The interface to fetch the data from OpenWeatherMap. Defaults to
82      *                                          CurlFetcher() if cURL is available. Otherwise defaults to
83      *                                          FileGetContentsFetcher() using 'file_get_contents()'.
84      * @param bool|string           $cacheClass If set to false, caching is disabled. Otherwise this must be a class
85      *                                          extending AbstractCache. Defaults to false.
86      * @param int                   $seconds    How long weather data shall be cached. Default 10 minutes.
87      *
88      * @throws \Exception If $cache is neither false nor a valid callable extending Cmfcmf\OpenWeatherMap\Util\Cache.
89      * @api
90      */
91     public function __construct($fetcher = null, $cacheClass = false, $seconds = 600)
92     {
93         if ($cacheClass !== false && !($cacheClass instanceof AbstractCache)) {
94             throw new \Exception("The cache class must implement the FetcherInterface!");
95         }
96         if (!is_numeric($seconds)) {
97             throw new \Exception("\$seconds must be numeric.");
98         }
99         if (!isset($fetcher)) {
100             $fetcher = (function_exists('curl_version')) ? new CurlFetcher() : new FileGetContentsFetcher();
101         }
102         if ($seconds == 0) {
103             $cacheClass = false;
104         }
105
106         $this->cacheClass = $cacheClass;
107         $this->seconds = $seconds;
108         $this->fetcher = $fetcher;
109     }
110
111     /**
112      * Returns the current weather at the place you specified as an object.
113      *
114      * @param array|int|string $query The place to get weather information for. For possible values see below.
115      * @param string           $units Can be either 'metric' or 'imperial' (default). This affects almost all units returned.
116      * @param string           $lang  The language to use for descriptions, default is 'en'. For possible values see below.
117      * @param string           $appid Your app id, default ''. See http://openweathermap.org/appid for more details.
118      *
119      * @throws OpenWeatherMap\Exception If OpenWeatherMap returns an error.
120      * @throws \InvalidArgumentException If an argument error occurs.
121      *
122      * @return CurrentWeather The weather object.
123      *
124      * There are three ways to specify the place to get weather information for:
125      * - Use the city name: $query must be a string containing the city name.
126      * - Use the city id: $query must be an integer containing the city id.
127      * - Use the coordinates: $query must be an associative array containing the 'lat' and 'lon' values.
128      *
129      * Available languages are (as of 17. July 2013):
130      * - English - en
131      * - Russian - ru
132      * - Italian - it
133      * - Spanish - sp
134      * - Ukrainian - ua
135      * - German - de
136      * - Portuguese - pt
137      * - Romanian - ro
138      * - Polish - pl
139      * - Finnish - fi
140      * - Dutch - nl
141      * - French - fr
142      * - Bulgarian - bg
143      * - Swedish - se
144      * - Chinese Traditional - zh_tw
145      * - Chinese Simplified - zh_cn
146      * - Turkish - tr
147      *
148      * @api
149      */
150     public function getWeather($query, $units = 'imperial', $lang = 'en', $appid = '')
151     {
152         // Disable default error handling of SimpleXML (Do not throw E_WARNINGs).
153         libxml_use_internal_errors(true);
154         libxml_clear_errors();
155
156         $answer = $this->getRawWeatherData($query, $units, $lang, $appid, 'xml');
157
158         try {
159             $xml = new \SimpleXMLElement($answer);
160         } catch (\Exception $e) {
161             // Invalid xml format. This happens in case OpenWeatherMap returns an error.
162             // OpenWeatherMap always uses json for errors, even if one specifies xml as format.
163             $error = json_decode($answer, true);
164             if (isset($error['message'])) {
165                 throw new OWMException($error['message'], $error['cod']);
166             } else {
167                 throw new OWMException('Unknown fatal error: OpenWeatherMap returned the following json object: ' . $answer);
168             }
169         }
170
171         return new CurrentWeather($xml, $units);
172     }
173
174     /**
175      * Returns the current weather at the place you specified as an object.
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 below.
180      * @param string           $appid Your app id, default ''. See http://openweathermap.org/appid for more details.
181      * @param int              $days  For how much days you want to get a forecast. Default 1, maximum: 16.
182      *
183      * @throws OpenWeatherMap\Exception If OpenWeatherMap returns an error.
184      * @throws \InvalidArgumentException If an argument error occurs.
185      *
186      * @return WeatherForecast The WeatherForecast object.
187      *
188      * There are three ways to specify the place to get weather information for:
189      * - Use the city name: $query must be a string containing the city name.
190      * - Use the city id: $query must be an integer containing the city id.
191      * - Use the coordinates: $query must be an associative array containing the 'lat' and 'lon' values.
192      *
193      * Available languages are (as of 17. July 2013):
194      * - English - en
195      * - Russian - ru
196      * - Italian - it
197      * - Spanish - sp
198      * - Ukrainian - ua
199      * - German - de
200      * - Portuguese - pt
201      * - Romanian - ro
202      * - Polish - pl
203      * - Finnish - fi
204      * - Dutch - nl
205      * - French - fr
206      * - Bulgarian - bg
207      * - Swedish - se
208      * - Chinese Traditional - zh_tw
209      * - Chinese Simplified - zh_cn
210      * - Turkish - tr
211      *
212      * @api
213      */
214     public function getWeatherForecast($query, $units = 'imperial', $lang = 'en', $appid = '', $days = 1)
215     {
216         // Disable default error handling of SimpleXML (Do not throw E_WARNINGs).
217         libxml_use_internal_errors(true);
218         libxml_clear_errors();
219
220         if ($days <= 5) {
221             $answer = $this->getRawHourlyForecastData($query, $units, $lang, $appid, 'xml');
222         } else if ($days <= 16) {
223             $answer = $this->getRawDailyForecastData($query, $units, $lang, $appid, 'xml', $days);
224         } else {
225             throw new \InvalidArgumentException('Error: forecasts are only available for the next 16 days. $days must be lower than 17.');
226         }
227
228         try {
229             $xml = new \SimpleXMLElement($answer);
230         } catch (\Exception $e) {
231             // Invalid xml format. This happens in case OpenWeatherMap returns an error.
232             // OpenWeatherMap always uses json for errors, even if one specifies xml as format.
233             $error = json_decode($answer, true);
234             if (isset($error['message'])) {
235                 throw new OWMException($error['message'], $error['cod']);
236             } else {
237                 throw new OWMException('Unknown fatal error: OpenWeatherMap returned the following json object: ' . $answer);
238             }
239         }
240
241         return new WeatherForecast($xml, $units, $days);
242     }
243
244     /**
245      * Returns the weather history for the place you specified as an object.
246      *
247      * @param array|int|string $query The place to get weather information for. For possible values see below.
248      * @param \DateTime        $start
249      * @param int              $endOrCount
250      * @param string           $type
251      * @param string           $units Can be either 'metric' or 'imperial' (default). This affects almost all units returned.
252      * @param string           $lang  The language to use for descriptions, default is 'en'. For possible values see below.
253      * @param string           $appid Your app id, default ''. See http://openweathermap.org/appid for more details.
254      *
255      * @throws OpenWeatherMap\Exception If OpenWeatherMap returns an error.
256      * @throws \InvalidArgumentException If an argument error occurs.
257      *
258      * @return WeatherHistory The WeatherHistory object.
259      *
260      * There are three ways to specify the place to get weather information for:
261      * - Use the city name: $query must be a string containing the city name.
262      * - Use the city id: $query must be an integer containing the city id.
263      * - Use the coordinates: $query must be an associative array containing the 'lat' and 'lon' values.
264      *
265      * Available languages are (as of 17. July 2013):
266      * - English - en
267      * - Russian - ru
268      * - Italian - it
269      * - Spanish - sp
270      * - Ukrainian - ua
271      * - German - de
272      * - Portuguese - pt
273      * - Romanian - ro
274      * - Polish - pl
275      * - Finnish - fi
276      * - Dutch - nl
277      * - French - fr
278      * - Bulgarian - bg
279      * - Swedish - se
280      * - Chinese Traditional - zh_tw
281      * - Chinese Simplified - zh_cn
282      * - Turkish - tr
283      *
284      * @api
285      */
286     public function getWeatherHistory($query, \DateTime $start, $endOrCount = 1, $type = 'hour', $units = 'imperial', $lang = 'en', $appid = '')
287     {
288         if (!in_array($type, array('tick', 'hour', 'day'))) {
289             throw new \InvalidArgumentException('$type must be either "tick", "hour" or "day"');
290         }
291
292         $xml = json_decode($this->getRawWeatherHistory($query, $start, $endOrCount, $type, $units, $lang, $appid), true);
293
294         if ($xml['cod'] != 200) {
295             throw new OWMException($xml['message'], $xml['cod']);
296         }
297
298         return new WeatherHistory($xml, $query);
299     }
300
301     /**
302      * @deprecated Use {@link self::getRawWeatherData()} instead.
303      */
304     public function getRawData($query, $units = 'imperial', $lang = 'en', $appid = '', $mode = 'xml')
305     {
306         return $this->getRawWeatherData($query, $units, $lang, $appid, $mode);
307     }
308
309     /**
310      * Directly returns the xml/json/html string returned by OpenWeatherMap for the current weather.
311      *
312      * @param array|int|string $query The place to get weather information for. For possible values see below.
313      * @param string           $units Can be either 'metric' or 'imperial' (default). This affects almost all units returned.
314      * @param string           $lang  The language to use for descriptions, default is 'en'. For possible values see below.
315      * @param string           $appid Your app id, default ''. See http://openweathermap.org/appid for more details.
316      * @param string           $mode  The format of the data fetched. Possible values are 'json', 'html' and 'xml' (default).
317      *
318      * @return string Returns false on failure and the fetched data in the format you specified on success.
319      *
320      * Warning If an error occurred, OpenWeatherMap returns data in json format ALWAYS
321      *
322      * There are three ways to specify the place to get weather information for:
323      * - Use the city name: $query must be a string containing the city name.
324      * - Use the city id: $query must be an integer containing the city id.
325      * - Use the coordinates: $query must be an associative array containing the 'lat' and 'lon' values.
326      *
327      * Available languages are (as of 17. July 2013):
328      * - English - en
329      * - Russian - ru
330      * - Italian - it
331      * - Spanish - sp
332      * - Ukrainian - ua
333      * - German - de
334      * - Portuguese - pt
335      * - Romanian - ro
336      * - Polish - pl
337      * - Finnish - fi
338      * - Dutch - nl
339      * - French - fr
340      * - Bulgarian - bg
341      * - Swedish - se
342      * - Chinese Traditional - zh_tw
343      * - Chinese Simplified - zh_cn
344      * - Turkish - tr
345      *
346      * @api
347      */
348     public function getRawWeatherData($query, $units = 'imperial', $lang = 'en', $appid = '', $mode = 'xml')
349     {
350         $url = $this->buildUrl($query, $units, $lang, $appid, $mode, $this->weatherUrl);
351
352         return $this->cacheOrFetchResult($url);
353     }
354
355     /**
356      * Directly returns the xml/json/html string returned by OpenWeatherMap for the hourly forecast.
357      *
358      * @param array|int|string $query The place to get weather information for. For possible values see below.
359      * @param string           $units Can be either 'metric' or 'imperial' (default). This affects almost all units returned.
360      * @param string           $lang  The language to use for descriptions, default is 'en'. For possible values see below.
361      * @param string           $appid Your app id, default ''. See http://openweathermap.org/appid for more details.
362      * @param string           $mode  The format of the data fetched. Possible values are 'json', 'html' and 'xml' (default).
363      *
364      * @return string Returns false on failure and the fetched data in the format you specified on success.
365      *
366      * Warning If an error occurred, OpenWeatherMap returns data in json format ALWAYS
367      *
368      * There are three ways to specify the place to get weather information for:
369      * - Use the city name: $query must be a string containing the city name.
370      * - Use the city id: $query must be an integer containing the city id.
371      * - Use the coordinates: $query must be an associative array containing the 'lat' and 'lon' values.
372      *
373      * Available languages are (as of 17. July 2013):
374      * - English - en
375      * - Russian - ru
376      * - Italian - it
377      * - Spanish - sp
378      * - Ukrainian - ua
379      * - German - de
380      * - Portuguese - pt
381      * - Romanian - ro
382      * - Polish - pl
383      * - Finnish - fi
384      * - Dutch - nl
385      * - French - fr
386      * - Bulgarian - bg
387      * - Swedish - se
388      * - Chinese Traditional - zh_tw
389      * - Chinese Simplified - zh_cn
390      * - Turkish - tr
391      *
392      * @api
393      */
394     public function getRawHourlyForecastData($query, $units = 'imperial', $lang = 'en', $appid = '', $mode = 'xml')
395     {
396         $url = $this->buildUrl($query, $units, $lang, $appid, $mode, $this->weatherHourlyForecastUrl);
397
398         return $this->cacheOrFetchResult($url);
399     }
400
401     /**
402      * Directly returns the xml/json/html string returned by OpenWeatherMap for the daily forecast.
403      *
404      * @param array|int|string $query The place to get weather information for. For possible values see below.
405      * @param string           $units Can be either 'metric' or 'imperial' (default). This affects almost all units returned.
406      * @param string           $lang  The language to use for descriptions, default is 'en'. For possible values see below.
407      * @param string           $appid Your app id, default ''. See http://openweathermap.org/appid for more details.
408      * @param string           $mode  The format of the data fetched. Possible values are 'json', 'html' and 'xml' (default)
409      * @param int              $cnt   How many days of forecast shall be returned? Maximum (and default): 16
410      *
411      * @throws \InvalidArgumentException If $cnt is higher than 16.
412      * @return string Returns false on failure and the fetched data in the format you specified on success.
413      *
414      * Warning If an error occurred, OpenWeatherMap returns data in json format ALWAYS
415      *
416      * There are three ways to specify the place to get weather information for:
417      * - Use the city name: $query must be a string containing the city name.
418      * - Use the city id: $query must be an integer containing the city id.
419      * - Use the coordinates: $query must be an associative array containing the 'lat' and 'lon' values.
420      *
421      * Available languages are (as of 17. July 2013):
422      * - English - en
423      * - Russian - ru
424      * - Italian - it
425      * - Spanish - sp
426      * - Ukrainian - ua
427      * - German - de
428      * - Portuguese - pt
429      * - Romanian - ro
430      * - Polish - pl
431      * - Finnish - fi
432      * - Dutch - nl
433      * - French - fr
434      * - Bulgarian - bg
435      * - Swedish - se
436      * - Chinese Traditional - zh_tw
437      * - Chinese Simplified - zh_cn
438      * - Turkish - tr
439      *
440      * @api
441      */
442     public function getRawDailyForecastData($query, $units = 'imperial', $lang = 'en', $appid = '', $mode = 'xml', $cnt = 16)
443     {
444         if ($cnt > 16) {
445             throw new \InvalidArgumentException('$cnt must be 16 or below!');
446         }
447         $url = $this->buildUrl($query, $units, $lang, $appid, $mode, $this->weatherDailyForecastUrl) . "&cnt=$cnt";
448
449         return $this->cacheOrFetchResult($url);
450     }
451
452     /**
453      * Directly returns the xml/json/html string returned by OpenWeatherMap for the daily forecast.
454      *
455      * @param array|int|string $query           The place to get weather information for. For possible values see below.
456      * @param \DateTime        $start           The \DateTime object of the date to get the first weather information from.
457      * @param \DateTime|int    $endOrCount      Can be either a \DateTime object representing the end of the period to
458      *                                          receive weather history data for or an integer counting the number of
459      *                                          reports requested.
460      * @param string           $type            The period of the weather history requested. Can be either be either "tick",
461      *                                          "hour" or "day".
462      * @param string           $units           Can be either 'metric' or 'imperial' (default). This affects almost all units returned.
463      * @param string           $lang            The language to use for descriptions, default is 'en'. For possible values see below.
464      * @param string           $appid           Your app id, default ''. See http://openweathermap.org/appid for more details.
465      *
466      * @throws \InvalidArgumentException
467      *
468      * @return string Returns false on failure and the fetched data in the format you specified on success.
469      *
470      * Warning If an error occurred, OpenWeatherMap returns data in json format ALWAYS
471      *
472      * There are three ways to specify the place to get weather information for:
473      * - Use the city name: $query must be a string containing the city name.
474      * - Use the city id: $query must be an integer containing the city id.
475      * - Use the coordinates: $query must be an associative array containing the 'lat' and 'lon' values.
476      *
477      * Available languages are (as of 17. July 2013):
478      * - English - en
479      * - Russian - ru
480      * - Italian - it
481      * - Spanish - sp
482      * - Ukrainian - ua
483      * - German - de
484      * - Portuguese - pt
485      * - Romanian - ro
486      * - Polish - pl
487      * - Finnish - fi
488      * - Dutch - nl
489      * - French - fr
490      * - Bulgarian - bg
491      * - Swedish - se
492      * - Chinese Traditional - zh_tw
493      * - Chinese Simplified - zh_cn
494      * - Turkish - tr
495      *
496      * @api
497      */
498     public function getRawWeatherHistory($query, \DateTime $start, $endOrCount = 1, $type = 'hour', $units = 'imperial', $lang = 'en', $appid = '')
499     {
500         if (!in_array($type, array('tick', 'hour', 'day'))) {
501             throw new \InvalidArgumentException('$type must be either "tick", "hour" or "day"');
502         }
503
504         $queryUrl = $this->weatherHistoryUrl . $this->buildQueryUrlParameter($query) . "&start={$start->format('U')}";
505
506         if ($endOrCount instanceof \DateTime) {
507             $queryUrl .= "&end={$endOrCount->format('U')}";
508         } else if (is_numeric($endOrCount) && $endOrCount > 0) {
509             $queryUrl .= "&cnt=$endOrCount";
510         } else {
511             throw new \InvalidArgumentException('$endOrCount must be either a \DateTime or a positive integer.');
512         }
513         $queryUrl .= "&type=$type&units=$units&lang=$lang";
514
515         if (!empty($appid)) {
516             $queryUrl .= "&APPID=$appid";
517         }
518
519         return $this->cacheOrFetchResult($queryUrl);
520     }
521
522     /**
523      * Fetches the result or delivers a cached version of the result.
524      *
525      * @param $url
526      *
527      * @return string
528      *
529      * @internal
530      */
531     private function cacheOrFetchResult($url)
532     {
533         if ($this->cacheClass !== false) {
534             /** @var \Cmfcmf\OpenWeatherMap\AbstractCache $cache */
535             $cache = $this->cacheClass;
536             $cache->setSeconds($this->seconds);
537             if ($cache->isCached($url)) {
538                 return $cache->getCached($url);
539             }
540             $result = $this->fetcher->fetch($url);
541             $cache->setCached($url, $result);
542         } else {
543             $result = $this->fetcher->fetch($url);
544         }
545
546         return $result;
547     }
548
549     /**
550      * Build the url to fetch weather data from.
551      *
552      * @param        $query
553      * @param        $units
554      * @param        $lang
555      * @param        $appid
556      * @param        $mode
557      * @param string $url The url to prepend.
558      *
559      * @return bool|string The fetched url, false on failure.
560      *
561      * @internal
562      */
563     private function buildUrl($query, $units, $lang, $appid, $mode, $url)
564     {
565         $queryUrl = $this->buildQueryUrlParameter($query);
566
567         $url = $url . "$queryUrl&units=$units&lang=$lang&mode=$mode";
568         if (!empty($appid)) {
569             $url .= "&APPID=$appid";
570         }
571
572         return $url;
573     }
574
575     /**
576      * Builds the query string for the url.
577      *
578      * @param $query
579      *
580      * @return string The built query string for the url.
581      * @throws \InvalidArgumentException If the query parameter is invalid.
582      *
583      * @internal
584      */
585     private function buildQueryUrlParameter($query)
586     {
587         switch ($query) {
588             case (is_array($query) && isset($query['lat']) && isset($query['lon']) && is_numeric($query['lat']) && is_numeric($query['lon'])):
589                 return "lat={$query['lat']}&lon={$query['lon']}";
590             case (is_numeric($query)):
591                 return "id=$query";
592             case (is_string($query)):
593                 return "q=" . urlencode($query);
594             default:
595                 throw new \InvalidArgumentException('Error: $query has the wrong format. See the documentation of OpenWeatherMap::getRawData() to read about valid formats.');
596         }
597     }
598 }