diff --git a/.travis.yml b/.travis.yml index 29e9915..7ce60de 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,8 +5,8 @@ language: php matrix: include: - - php: hhvm - php: nightly + - php: 7.3 - php: 7.2 - php: 7.1 - php: 7.0 @@ -19,11 +19,15 @@ matrix: allow_failures: - php: nightly -install: - - if [[ ! $TRAVIS_PHP_VERSION = hhvm* ]]; then phpenv config-rm xdebug.ini || echo "xdebug not available"; fi - - if [[ ! $TRAVIS_PHP_VERSION = hhvm* ]]; then INI_FILE=~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/travis.ini; else INI_FILE=/etc/hhvm/php.ini; fi +before_install: + - phpenv config-rm xdebug.ini || echo "xdebug not available" + - INI_FILE=~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/travis.ini - echo date.timezone = Europe/Berlin >> $INI_FILE - echo memory_limit = -1 >> $INI_FILE + +install: + # Use composer update instead of composer install to install a working set of + # dependencies on all PHP versions. - composer update script: diff --git a/Cmfcmf/OpenWeatherMap.php b/Cmfcmf/OpenWeatherMap.php index 500dfa7..4ff6ccb 100644 --- a/Cmfcmf/OpenWeatherMap.php +++ b/Cmfcmf/OpenWeatherMap.php @@ -71,7 +71,7 @@ class OpenWeatherMap /** * @var string The basic api url to fetch uv index data from. */ - private $uvIndexUrl = 'https://api.openweathermap.org/v3/uvi'; + private $uvIndexUrl = 'https://api.openweathermap.org/data/2.5/uvi'; /** * @var AbstractCache|bool $cache The cache to use. @@ -315,47 +315,73 @@ public function getWeatherHistory($query, \DateTime $start, $endOrCount = 1, $ty /** * Returns the current uv index at the location you specified. * - * @param float $lat The location's latitude. - * @param float $lon The location's longitude. + * @param float $lat The location's latitude. + * @param float $lon The location's longitude. * * @throws OpenWeatherMap\Exception If OpenWeatherMap returns an error. * @throws \InvalidArgumentException If an argument error occurs. * - * @return UVIndex The uvi object. + * @return UVIndex * * @api */ public function getCurrentUVIndex($lat, $lon) { - $answer = $this->getRawCurrentUVIndexData($lat, $lon); + $answer = $this->getRawUVIndexData('current', $lat, $lon); $json = $this->parseJson($answer); return new UVIndex($json); } /** - * Returns the uv index at date, time and location you specified. + * Returns a forecast of the uv index at the specified location. + * The optional $cnt parameter determines the number of days to forecase. + * The maximum supported number of days is 8. * - * @param float $lat The location's latitude. - * @param float $lon The location's longitude. - * @param \DateTimeInterface $dateTime The date and time to request data for. - * @param string $timePrecision This decides about the timespan OWM will look for the uv index. The tighter - * the timespan, the less likely it is to get a result. Can be 'year', 'month', - * 'day', 'hour', 'minute' or 'second', defaults to 'day'. + * @param float $lat The location's latitude. + * @param float $lon The location's longitude. + * @param int $cnt Number of returned days (default to 8). * * @throws OpenWeatherMap\Exception If OpenWeatherMap returns an error. * @throws \InvalidArgumentException If an argument error occurs. * - * @return UVIndex The uvi object. + * @return UVIndex[] * * @api */ - public function getUVIndex($lat, $lon, $dateTime, $timePrecision = 'day') + public function getForecastUVIndex($lat, $lon, $cnt = 8) { - $answer = $this->getRawUVIndexData($lat, $lon, $dateTime, $timePrecision); - $json = $this->parseJson($answer); + $answer = $this->getRawUVIndexData('forecast', $lat, $lon, $cnt); + $data = $this->parseJson($answer); - return new UVIndex($json); + return array_map(function ($entry) { + return new UVIndex($entry); + }, $data); + } + + /** + * Returns the historic uv index at the specified location. + * + * @param float $lat The location's latitude. + * @param float $lon The location's longitude. + * @param \DateTime $start Starting point of time period. + * @param \DateTime $end Final point of time period. + * + * @throws OpenWeatherMap\Exception If OpenWeatherMap returns an error. + * @throws \InvalidArgumentException If an argument error occurs. + * + * @return UVIndex[] + * + * @api + */ + public function getHistoricUVIndex($lat, $lon, $start, $end) + { + $answer = $this->getRawUVIndexData('historic', $lat, $lon, null, $start, $end); + $data = $this->parseJson($answer); + + return array_map(function ($entry) { + return new UVIndex($entry); + }, $data); } /** @@ -492,55 +518,45 @@ public function getRawWeatherHistory($query, \DateTime $start, $endOrCount = 1, } /** - * Directly returns the json string returned by OpenWeatherMap for the current UV index data. + * Directly returns the json string returned by OpenWeatherMap for the UV index data. * - * @param float $lat The location's latitude. - * @param float $lon The location's longitude. + * @param string $mode The type of requested data (['historic', 'forecast', 'current']). + * @param float $lat The location's latitude. + * @param float $lon The location's longitude. + * @param int $cnt Number of returned days (only allowed for 'forecast' data). + * @param \DateTime $start Starting point of time period (only allowed and required for 'historic' data). + * @param \DateTime $end Final point of time period (only allowed and required for 'historic' data). * * @return bool|string Returns the fetched data. * * @api */ - public function getRawCurrentUVIndexData($lat, $lon) + public function getRawUVIndexData($mode, $lat, $lon, $cnt = null, $start = null, $end = null) { - if (!$this->apiKey) { - throw new \RuntimeException('Before using this method, you must set the api key using ->setApiKey()'); + if (!in_array($mode, array('current', 'forecast', 'historic'), true)) { + throw new \InvalidArgumentException("$mode must be one of 'historic', 'forecast', 'current'."); } if (!is_float($lat) || !is_float($lon)) { throw new \InvalidArgumentException('$lat and $lon must be floating point numbers'); } - $url = $this->buildUVIndexUrl($lat, $lon); - - return $this->cacheOrFetchResult($url); - } - - /** - * Directly returns the json string returned by OpenWeatherMap for the UV index data. - * - * @param float $lat The location's latitude. - * @param float $lon The location's longitude. - * @param \DateTimeInterface $dateTime The date and time to request data for. - * @param string $timePrecision This decides about the timespan OWM will look for the uv index. The tighter - * the timespan, the less likely it is to get a result. Can be 'year', 'month', - * 'day', 'hour', 'minute' or 'second', defaults to 'day'. - * - * @return bool|string Returns the fetched data. - * - * @api - */ - public function getRawUVIndexData($lat, $lon, $dateTime, $timePrecision = 'day') - { - if (!$this->apiKey) { - throw new \RuntimeException('Before using this method, you must set the api key using ->setApiKey()'); + if (isset($cnt) && (!is_int($cnt) || $cnt > 8 || $cnt < 1)) { + throw new \InvalidArgumentException('$cnt must be an int between 1 and 8'); } - if (!is_float($lat) || !is_float($lon)) { - throw new \InvalidArgumentException('$lat and $lon must be floating point numbers'); + if (isset($start) && !$start instanceof \DateTime) { + throw new \InvalidArgumentException('$start must be an instance of \DateTime'); + } + if (isset($end) && !$end instanceof \DateTime) { + throw new \InvalidArgumentException('$end must be an instance of \DateTime'); } - if (interface_exists('DateTimeInterface') && !$dateTime instanceof \DateTimeInterface || !$dateTime instanceof \DateTime) { - throw new \InvalidArgumentException('$dateTime must be an instance of \DateTime or \DateTimeInterface'); + if ($mode === 'current' && (isset($start) || isset($end) || isset($cnt))) { + throw new \InvalidArgumentException('Neither $start, $end, nor $cnt must be set for current data.'); + } elseif ($mode === 'forecast' && (isset($start) || isset($end) || !isset($cnt))) { + throw new \InvalidArgumentException('$cnt needs to be set and both $start and $end must not be set for forecast data.'); + } elseif ($mode === 'historic' && (!isset($start) || !isset($end) || isset($cnt))) { + throw new \InvalidArgumentException('Both $start and $end need to be set and $cnt must not be set for historic data.'); } - $url = $this->buildUVIndexUrl($lat, $lon, $dateTime, $timePrecision); + $url = $this->buildUVIndexUrl($mode, $lat, $lon, $cnt, $start, $end); return $this->cacheOrFetchResult($url); } @@ -575,10 +591,12 @@ private function cacheOrFetchResult($url) /** @var AbstractCache $cache */ $cache = $this->cache; $cache->setSeconds($this->seconds); + if ($cache->isCached($url)) { $this->wasCached = true; return $cache->getCached($url); } + $result = $this->fetcher->fetch($url); $cache->setCached($url, $result); } else { @@ -612,47 +630,41 @@ private function buildUrl($query, $units, $lang, $appid, $mode, $url) } /** - * @param float $lat - * @param float $lon - * @param \DateTime|\DateTimeImmutable $dateTime - * @param string $timePrecision + * @param string $mode The type of requested data. + * @param float $lat The location's latitude. + * @param float $lon The location's longitude. + * @param int $cnt Number of returned days. + * @param \DateTime $start Starting point of time period. + * @param \DateTime $end Final point of time period. * * @return string */ - private function buildUVIndexUrl($lat, $lon, $dateTime = null, $timePrecision = null) + private function buildUVIndexUrl($mode, $lat, $lon, $cnt = null, \DateTime $start = null, \DateTime $end = null) { - if ($dateTime !== null) { - $format = '\Z'; - switch ($timePrecision) { - /** @noinspection PhpMissingBreakStatementInspection */ - case 'second': - $format = ':s' . $format; - /** @noinspection PhpMissingBreakStatementInspection */ - case 'minute': - $format = ':i' . $format; - /** @noinspection PhpMissingBreakStatementInspection */ - case 'hour': - $format = '\TH' . $format; - /** @noinspection PhpMissingBreakStatementInspection */ - case 'day': - $format = '-d' . $format; - /** @noinspection PhpMissingBreakStatementInspection */ - case 'month': - $format = '-m' . $format; - case 'year': - $format = 'Y' . $format; - break; - default: - throw new \InvalidArgumentException('$timePrecision is invalid.'); - } - // OWM only accepts UTC timezones. - $dateTime->setTimezone(new \DateTimeZone('UTC')); - $dateTime = $dateTime->format($format); - } else { - $dateTime = 'current'; + $params = array( + 'appid' => $this->apiKey, + 'lat' => $lat, + 'lon' => $lon, + ); + + switch ($mode) { + case 'historic': + $requestMode = '/history'; + $params['start'] = $start->format('U'); + $params['end'] = $end->format('U'); + break; + case 'forecast': + $requestMode = '/forecast'; + $params['cnt'] = $cnt; + break; + case 'current': + $requestMode = ''; + break; + default: + throw new \InvalidArgumentException("Invalid mode $mode for uv index url"); } - return sprintf($this->uvIndexUrl . '/%s,%s/%s.json?appid=%s', $lat, $lon, $dateTime, $this->apiKey); + return sprintf($this->uvIndexUrl . '%s?%s', $requestMode, http_build_query($params)); } /** @@ -718,7 +730,8 @@ private function parseJson($answer) { $json = json_decode($answer); if (json_last_error() !== JSON_ERROR_NONE) { - throw new OWMException('OpenWeatherMap returned an invalid json object. JSON error was: ' . $this->json_last_error_msg()); + throw new OWMException('OpenWeatherMap returned an invalid json object. JSON error was: "' . + $this->json_last_error_msg() . '". The retrieved json was: ' . $answer); } if (isset($json->message)) { throw new OWMException('An error occurred: '. $json->message); diff --git a/Cmfcmf/OpenWeatherMap/UVIndex.php b/Cmfcmf/OpenWeatherMap/UVIndex.php index b89d109..928bd17 100644 --- a/Cmfcmf/OpenWeatherMap/UVIndex.php +++ b/Cmfcmf/OpenWeatherMap/UVIndex.php @@ -49,8 +49,8 @@ class UVIndex public function __construct($data) { $utctz = new \DateTimeZone('UTC'); - $this->time = new \DateTime($data->time, $utctz); - $this->location = new Location($data->location->latitude, $data->location->longitude); - $this->uvIndex = (float)$data->data; + $this->time = new \DateTime($data->date_iso, $utctz); + $this->location = new Location($data->lat, $data->lon); + $this->uvIndex = (float)$data->value; } } diff --git a/Examples/UVIndex.php b/Examples/UVIndex.php new file mode 100644 index 0000000..6f0d507 --- /dev/null +++ b/Examples/UVIndex.php @@ -0,0 +1,63 @@ +'; +if (php_sapi_name() === 'cli') { + $lf = "\n"; + $cli = true; +} + +// Language of data (try your own language here!): +$lang = 'de'; + +// Units (can be 'metric' or 'imperial' [default]): +$units = 'metric'; + +// Get OpenWeatherMap object. Don't use caching (take a look into Example_Cache.php to see how it works). +$owm = new OpenWeatherMap(); +$owm->setApiKey($myApiKey); + +// Example 1: Get current uv index in Berlin. +$uvIndex = $owm->getCurrentUVIndex(52.520008, 13.404954); +echo "EXAMPLE 1$lf"; + +echo "Current uv index: $uvIndex->uvIndex"; +echo $lf; + +// Example 2: Get uv index forecast in Berlin. +$forecast = $owm->getForecastUVIndex(52.520008, 13.404954); +echo "EXAMPLE 2$lf"; + +foreach ($forecast as $day) { + echo "{$day->time->format('r')} will have an uv index of: $day->uvIndex"; + echo $lf; +} + + +// Example 3: Get historic uv index in Berlin. +$history = $owm->getHistoricUVIndex(52.520008, 13.404954, new DateTime('-4month'), new DateTime('-3month')); +echo "EXAMPLE 3$lf"; + +foreach ($history as $day) { + echo "{$day->time->format('r')} had an uv index of: $day->uvIndex"; + echo $lf; +} diff --git a/tests/Exceptions/OpenWeatherMapExceptionTest.php b/tests/Exceptions/OpenWeatherMapExceptionTest.php index cb195bd..f17ce91 100644 --- a/tests/Exceptions/OpenWeatherMapExceptionTest.php +++ b/tests/Exceptions/OpenWeatherMapExceptionTest.php @@ -121,36 +121,9 @@ public function testGetRawWeatherHistoryWithEndDateException() * @expectedException \InvalidArgumentException * @dataProvider uvIndexExceptionDataProvider */ - public function testGetRawUVIndexWithQueryErrorException($lat, $lon, $dateTime, $precision) + public function testGetRawUVIndexWithQueryErrorException($mode, $lat, $lon, $cnt, $start, $end) { - $this->owm->getRawUVIndexData($lat, $lon, $dateTime, $precision); - } - - /** - * @expectedException \InvalidArgumentException - * @dataProvider currentUVIndexExceptionDataProvider - */ - public function testGetRawCurrentUVIndexWithQueryErrorException($lat, $lon) - { - $this->owm->getRawCurrentUVIndexData($lat, $lon); - } - - /** - * @expectedException \RuntimeException - */ - public function testGetRawUVIndexWithoutApiKey() - { - $this->owm->setApiKey(null); - $this->owm->getRawUVIndexData(1.1, 1.1, new \DateTime()); - } - - /** - * @expectedException \RuntimeException - */ - public function testGetRawCurrentUVIndexWithoutApiKey() - { - $this->owm->setApiKey(null); - $this->owm->getRawCurrentUVIndexData(1.1, 1.1); + $this->owm->getRawUVIndexData($mode, $lat, $lon, $cnt, $start, $end); } /** @@ -198,22 +171,20 @@ public function testParseJsonException() $method->invoke($this->owm, $answer); } + /** + * @expectedException \Cmfcmf\OpenWeatherMap\Exception + */ public function uvIndexExceptionDataProvider() { return array( - array('error-query-format', 'foo', new \DateTime(), 'year'), - array(5.4, 1.2, 'foo', 'month'), - array(5.4, 12, 'foo', 'day'), - array(5.4, 1.2, 'foo', 'bar'), - ); - } - - public function currentUVIndexExceptionDataProvider() - { - return array( - array('error-query-format', 'foo'), - array(5.4, 12), - array(5.4, '1.2'), + array('current', 5.4, 1, 5, null, null), + array('forecast', 5.4, 1.2, '5', null, null), + array('forecast', 5.4, 1.2, 0, null, null), + array('forecast', 5.4, 1.2, 9, null, null), + array('forecast', 5.4, 1.2, 5, new \DateTime(), new \DateTime()), + array('forecast', 5.4, 12.0, null, '2000-1-1', null), + array('historic', 5.4, 1.2, null, new \DateTime(), '2000-1-1'), + array('historic', 5.4, 1.2, 5, new \DateTime(), new \DateTime()), ); } } diff --git a/tests/OpenWeatherMapTest.php b/tests/OpenWeatherMapTest.php index 6a27acc..ee67c18 100644 --- a/tests/OpenWeatherMapTest.php +++ b/tests/OpenWeatherMapTest.php @@ -138,19 +138,32 @@ public function testGetCurrentUVIndex() $this->assertInstanceOf('\Cmfcmf\OpenWeatherMap\UVIndex', $result); } - public function testGetUVIndex() + public function testGetForecastUVIndex() { $owm = $this->openWeather; - $precisions = array('year', 'month', 'day', 'hour', 'minute', 'second'); - foreach ($precisions as $precision) { - try { - $result = $owm->getUVIndex(40.7, -74.2, new \DateTime(), $precision); - } catch (Exception $e) { - // OWM might not actually have data for the timespan. - $this->assertSame('An error occurred: not found', $e->getMessage()); - } - $this->assertInstanceOf('\Cmfcmf\OpenWeatherMap\UVIndex', $result); + + try { + $result = $owm->getForecastUVIndex(40.7, -74.2, 5); + } catch (Exception $e) { + // OWM might not actually have data for the timespan. + $this->assertSame('An error occurred: not found', $e->getMessage()); + } + $this->assertContainsOnlyInstancesOf('\Cmfcmf\OpenWeatherMap\UVIndex', $result); + } + + public function testGetHistoryUVIndex() + { + $owm = $this->openWeather; + + try { + $start = new \DateTime('1969-08-15'); + $end = new \DateTime('1969-08-18'); + $result = $owm->getHistoricUVIndex(40.7, -74.2, $start, $end); + } catch (Exception $e) { + // OWM might not actually have data for the timespan. + $this->assertSame('An error occurred: not found', $e->getMessage()); } + $this->assertContainsOnlyInstancesOf('\Cmfcmf\OpenWeatherMap\UVIndex', $result); } public function testGetDailyWeatherForecast()