diff --git a/Example_Cache.php b/Example_Cache.php new file mode 100644 index 0000000..d8d0198 --- /dev/null +++ b/Example_Cache.php @@ -0,0 +1,18 @@ +getWeather('Berlin', $units, $lang); +echo "EXAMPLE 1
\n\n\n"; +echo $weather->temperature; diff --git a/Examples.php b/Examples.php index e810bd2..e55c1bc 100644 --- a/Examples.php +++ b/Examples.php @@ -14,8 +14,10 @@ * @see http://openweathermap.org/appid */ -// Include api class -require_once('OpenWeatherMap.php'); +use cmfcmf\OpenWeatherMap; +use cmfcmf\OpenWeatherMap\Exception as OWMException; + +require('cmfcmf/OpenWeatherMap.php'); // Language of data (try your own language here!): $lang = 'de'; @@ -23,8 +25,11 @@ // 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(); + // Example 1: Get current temperature in Berlin. -$weather = OpenWeatherMap::getWeather('Berlin', $units, $lang); +$weather = $owm->getWeather('Berlin', $units, $lang); echo "EXAMPLE 1
\n\n\n"; // $weather contains all available weather information for Berlin. @@ -73,7 +78,7 @@ echo "
\n"; // Example 2: Get current pressure and humidity in Hongkong. -$weather = OpenWeatherMap::getWeather('Hongkong', $units, $lang); +$weather = $owm->getWeather('Hongkong', $units, $lang); echo "

\n\n\nEXAMPLE 2
\n\n\n"; /** @@ -98,14 +103,14 @@ echo "
\n"; // Example 4: Get current temperature from coordinates (Greenland :-) ). -$weather = OpenWeatherMap::getWeather(array('lat' => 77.73038, 'lon' => 41.89604), $units, $lang); +$weather = $owm->getWeather(array('lat' => 77.73038, 'lon' => 41.89604), $units, $lang); echo "

\n\n\nEXAMPLE 4
\n\n\n"; echo "Temperature: " . $weather->temperature; echo "
\n"; // Example 5: Get current temperature from city id. The city is an internal id used by OpenWeatherMap. See example 6 too. -$weather = OpenWeatherMap::getWeather(2172797, $units, $lang); +$weather = $owm->getWeather(2172797, $units, $lang); echo "

\n\n\nEXAMPLE 5
\n\n\n"; echo "City: " . $weather->city->name; @@ -115,7 +120,7 @@ echo "
\n"; // Example 6: Get information about a city. -$weather = OpenWeatherMap::getWeather('Paris', $units, $lang); +$weather = $owm->getWeather('Paris', $units, $lang); echo "

\n\n\nEXAMPLE 6
\n\n\n"; echo "Id: " . $weather->city->id; @@ -174,19 +179,19 @@ // Example 11: Get raw xml data. echo "

\n\n\nEXAMPLE 11
\n\n\n"; -echo "
" . htmlspecialchars(OpenWeatherMap::getRawData('Berlin', $units, $lang, null, 'xml')) . "
"; +echo "
" . htmlspecialchars($owm->getRawData('Berlin', $units, $lang, null, 'xml')) . "
"; echo "
\n"; // Example 12: Get raw json data. echo "

\n\n\nEXAMPLE 12
\n\n\n"; -echo "" . htmlspecialchars(OpenWeatherMap::getRawData('Berlin', $units, $lang, null, 'json')) . ""; +echo "" . htmlspecialchars($owm->getRawData('Berlin', $units, $lang, null, 'json')) . ""; echo "
\n"; // Example 13: Get raw html data. echo "

\n\n\nEXAMPLE 13
\n\n\n"; -echo OpenWeatherMap::getRawData('Berlin', $units, $lang, null, 'html'); +echo $owm->getRawData('Berlin', $units, $lang, null, 'html'); echo "
\n"; // Example 14: Error handling. @@ -194,32 +199,32 @@ // Try wrong city name. try { - $weather = OpenWeatherMap::getWeather("ThisCityNameIsNotValidAndDoesNotExist", $units, $lang); -} catch(OpenWeatherMap_Exception $e) { + $weather = $owm->getWeather("ThisCityNameIsNotValidAndDoesNotExist", $units, $lang); +} catch(OWMException $e) { echo $e->getMessage() . ' (Code ' . $e->getCode() . ').'; echo "
\n"; } // Try invalid $query. try { - $weather = OpenWeatherMap::getWeather(new DateTime('now'), $units, $lang); -} catch(Exception $e) { + $weather = $owm->getWeather(new \DateTime('now'), $units, $lang); +} catch(\Exception $e) { echo $e->getMessage() . ' (Code ' . $e->getCode() . ').'; echo "
\n"; } // Full error handling would look like this: try { - $weather = OpenWeatherMap::getWeather(-1, $units, $lang); -} catch(OpenWeatherMap_Exception $e) { + $weather = $owm->getWeather(-1, $units, $lang); +} catch(OWMException $e) { echo 'OpenWeatherMap exception: ' . $e->getMessage() . ' (Code ' . $e->getCode() . ').'; echo "
\n"; -} catch(Exception $e) { +} catch(\Exception $e) { echo 'General exception: ' . $e->getMessage() . ' (Code ' . $e->getCode() . ').'; echo "
\n"; } // Example 15: Using an api key: -# OpenWeatherMap::getWeather('Berlin', $units, $lang, 'Your-Api-Key-Here'); -# OpenWeatherMap::getRawData('Berlin', $units, $lang, 'Your-Api-Key-Here', 'json'); +# $owm->getWeather('Berlin', $units, $lang, 'Your-Api-Key-Here'); +# $owm->getRawData('Berlin', $units, $lang, 'Your-Api-Key-Here', 'json'); diff --git a/Util.php b/Util.php deleted file mode 100644 index 89880f3..0000000 --- a/Util.php +++ /dev/null @@ -1,271 +0,0 @@ -OpenWeatherMap.org"; - - public function __construct($query, $units = 'imperial', $lang = 'en', $appid = '') - { - // Disable default error handling of SimpleXML (Do not throw E_WARNINGs). - libxml_use_internal_errors(true); - libxml_clear_errors(); - - $answer = OpenWeatherMap::getRawData($query, $units, $lang, $appid, 'xml'); - if ($answer === false) { - // $query has the wrong format, throw error. - throw new Exception('Error: $query has the wrong format. See the documentation of OpenWeatherMap::getRawData() to read about valid formats.'); - } - - try { - $xml = new SimpleXMLElement($answer); - } catch(Exception $e) { - // Invalid xml format. This happens in case OpenWeatherMap returns an error. - // OpenWeatherMap always uses json for errors, even if one specifies xml as format. - $error = json_decode($answer, true); - if (isset($error['message'])) { - throw new OpenWeatherMap_Exception($error['message'], $error['cod']); - } else { - throw new OpenWeatherMap_Exception('Unknown fatal error: OpenWeatherMap returned the following json object: ' . print_r($error)); - } - } - - $this->city = new _City($xml->city['id'], $xml->city['name'], $xml->city->coord['lon'], $xml->city->coord['lat'], $xml->city->country); - $this->temperature = new _Temperature(new _Unit($xml->temperature['value'], $xml->temperature['unit']), new _Unit($xml->temperature['min'], $xml->temperature['unit']), new _Unit($xml->temperature['max'], $xml->temperature['unit'])); - $this->humidity = new _Unit($xml->humidity['value'], $xml->humidity['unit']); - $this->pressure = new _Unit($xml->pressure['value'], $xml->pressure['unit']); - - - // This is kind of a hack, because the units are missing in the xml document. - if ($units == 'metric') { - $windSpeedUnit = 'm/s'; - } else { - $windSpeedUnit = 'mph'; - } - $this->wind = new _Wind(new _Unit($xml->wind->speed['value'], $windSpeedUnit, $xml->wind->speed['name']), new _Unit($xml->wind->direction['value'], $xml->wind->direction['code'], $xml->wind->direction['name'])); - - - $this->clouds = new _Unit($xml->clouds['value'], null, $xml->clouds['name']); - $this->precipitation = new _Unit($xml->precipitation['value'], $xml->precipitation['unit'], $xml->precipitation['mode']); - $this->sun = new _Sun(new DateTime($xml->city->sun['rise']), new DateTime($xml->city->sun['set'])); - $this->weather = new _Weather($xml->weather['number'], $xml->weather['value'], $xml->weather['icon']); - $this->lastUpdate = new DateTime($xml->lastupdate['value']); - } -} - -/** - * @class General class for each value. - */ -class _Unit -{ - private $value; - - private $unit; - - private $description; - - public function __construct($value = 0.0, $unit = "", $description = "") - { - $this->value = (float)$value; - $this->unit = (string)$unit; - $this->description = (string)$description; - } - - public function __toString() - { - return $this->getFormatted(); - } - - public function getUnit() - { - // Units are inconsistent. Only celsius and kelvin are not abbreviated. This check fixes that. - if ($this->unit == 'celsius') { - return '°C'; - } else if ($this->unit == 'kelvin') { - return 'K'; - } else { - return $this->unit; - } - } - - public function getValue() - { - return $this->value; - } - - public function getDescription() - { - return $this->description; - } - - public function getFormatted() - { - if ($this->getUnit() != "") { - return "{$this->getValue()} {$this->getUnit()}"; - } else { - return "{$this->getValue()}"; - } - } -} - -class _Temperature -{ - public $now; - - public $min; - - public $max; - - public function __construct(_Unit $now, _Unit $min, _Unit $max) - { - $this->now = $now; - $this->min = $min; - $this->max = $max; - } - - public function __toString() - { - return $this->now->__toString(); - } - - public function getUnit() - { - return $this->now->getUnit(); - } - - public function getValue() - { - return $this->now->getValue(); - } - - public function getDescription() - { - return $this->now->getDescription(); - } - - public function getFormatted() - { - return $this->now->getFormatted(); - } -} - -class _Wind -{ - public $speed; - - public $direction; - - public function __construct(_Unit $speed, _Unit $direction) - { - $this->speed = $speed; - $this->direction = $direction; - } -} - -class _Sun -{ - public $rise; - - public $set; - - public function __construct(DateTime $rise, DateTime $set) - { - $this->rise = $rise; - $this->set = $set; - } -} - -class _City -{ - public $id; - - public $name; - - public $lon; - - public $lat; - - public $country; - - public function __construct($id, $name, $lon, $lat, $country) - { - $this->id = (int)$id; - $this->name = (string)$name; - $this->lon = (float)$lon; - $this->lat = (float)$lat; - $this->country = (string)$country; - } -} - -class _Weather -{ - public $id; - - public $description; - - public $icon; - - private $iconUrl = "http://openweathermap.org/img/w/%s.png"; - - public function __construct($id, $description, $icon) - { - $this->id = (int)$id; - $this->description = (string)$description; - $this->icon = (string)$icon; - } - - public function __toString() - { - return $this->description; - } - - public function getIconUrl() - { - return str_replace("%s", $this->icon, $this->iconUrl); - } -} - -class OpenWeatherMap_Exception extends Exception -{ - -} diff --git a/OpenWeatherMap.php b/cmfcmf/OpenWeatherMap.php similarity index 64% rename from OpenWeatherMap.php rename to cmfcmf/OpenWeatherMap.php index cdb9998..7e26141 100644 --- a/OpenWeatherMap.php +++ b/cmfcmf/OpenWeatherMap.php @@ -14,7 +14,14 @@ * @see http://openweathermap.org/appid */ -require_once('Util.php'); +namespace cmfcmf { + +use cmfcmf\OpenWeatherMap\Weather; + +# Install PSR-0-compatible class autoloader +spl_autoload_register(function($class){ + require preg_replace('{\\\\|_(?!.*\\\\)}', DIRECTORY_SEPARATOR, ltrim($class, '\\')).'.php'; +}); /** * Main static class for the OpenWeatherMap-PHP-API. @@ -22,10 +29,39 @@ class OpenWeatherMap { /** - * @const $url The basic api url to fetch data from. + * @var $url The basic api url to fetch data from. */ - const url = "http://api.openweathermap.org/data/2.5/weather?"; + private $url = "http://api.openweathermap.org/data/2.5/weather?"; + + private $cacheClass = false; + + private $seconds; + /** + * Constructs the OpenWeatherMap object. + * + * @param bool|string $cache If set to false, caching is disabled. If this is a valid class + * extending cmfcmf\OpenWeatherMap\Util\Cache, caching will be enabled. Default false. + * @param int $seconds How long weather data shall be cached. Default 10 minutes. + * + * @throws Exception If $cache is neither false nor a valid callable extending cmfcmf\OpenWeatherMap\Util\Cache. + */ + public function __construct($cacheClass = false, $seconds = 600) + { + if (!class_exists($cacheClass) && $cacheClass !== false) { + #throw new \Exception("Class $cacheClass does not exist."); + } + if (!is_numeric($seconds)) { + throw new \Exception("\$seconds must be numeric."); + } + if ($seconds == 0) { + $cacheClass = false; + } + + $this->cacheClass = $cacheClass; + $this->seconds = $seconds; + } + /** * Directly returns the xml/json/html string returned by OpenWeatherMap. * @@ -63,30 +99,45 @@ class OpenWeatherMap * - Chinese Simplified - zh_cn * - Turkish - tr */ - static public function getRawData($query, $units = 'imperial', $lang = 'en', $appid = '', $mode = 'xml') + public function getRawData($query, $units = 'imperial', $lang = 'en', $appid = '', $mode = 'xml') { switch($query) { case (is_array($query)): if (!is_numeric($query['lat']) || !is_numeric($query['lon'])) { return false; } - $query = "lat={$query['lat']}&lon={$query['lon']}"; + $queryUrl = "lat={$query['lat']}&lon={$query['lon']}"; break; case (is_numeric($query)): - $query = "id=$query"; + $queryUrl = "id=$query"; break; case (is_string($query)): - $query = "q=" . urlencode($query); + $queryUrl = "q=" . urlencode($query); break; default: return false; } - $url = self::url . "$query&units=$units&lang=$lang&mode=$mode"; + + $url = $this->url . "$queryUrl&units=$units&lang=$lang&mode=$mode"; if (!empty($appid)) { $url .= "&APPID=$appid"; } + + $result = ""; - return file_get_contents($url); + if ($this->cacheClass !== false) { + $cache = new $this->cacheClass; + $cache->setSeconds($this->seconds); + if ($cache->isCached($query, $units, $lang, $mode)) { + return $cache->getCached(); + } + $result = $this->fetch($url); + $cache->setCached($result, $query, $units, $lang, $mode); + } else { + $result = $this->fetch($url); + } + + return $result; } /** @@ -126,8 +177,15 @@ static public function getRawData($query, $units = 'imperial', $lang = 'en', $ap * - Chinese Simplified - zh_cn * - Turkish - tr */ - static public function getWeather($query, $units = 'imperial', $lang = 'en', $appid = '') + public function getWeather($query, $units = 'imperial', $lang = 'en', $appid = '') { - return new Weather($query, $units, $lang, $appid); + return new Weather($query, $units, $lang, $appid, $this->cacheClass, $this->seconds); } + + private function fetch($url) + { + return file_get_contents($url); + } +} + } diff --git a/cmfcmf/OpenWeatherMap/AbstractCache.php b/cmfcmf/OpenWeatherMap/AbstractCache.php new file mode 100644 index 0000000..7d97d7c --- /dev/null +++ b/cmfcmf/OpenWeatherMap/AbstractCache.php @@ -0,0 +1,74 @@ +seconds = $seconds; + } +} diff --git a/cmfcmf/OpenWeatherMap/Exception.php b/cmfcmf/OpenWeatherMap/Exception.php new file mode 100644 index 0000000..1031952 --- /dev/null +++ b/cmfcmf/OpenWeatherMap/Exception.php @@ -0,0 +1,26 @@ +id = (int)$id; + $this->name = (string)$name; + $this->lon = (float)$lon; + $this->lat = (float)$lat; + $this->country = (string)$country; + } +} diff --git a/cmfcmf/OpenWeatherMap/Util/Sun.php b/cmfcmf/OpenWeatherMap/Util/Sun.php new file mode 100644 index 0000000..3b112e3 --- /dev/null +++ b/cmfcmf/OpenWeatherMap/Util/Sun.php @@ -0,0 +1,30 @@ +rise = $rise; + $this->set = $set; + } +} diff --git a/cmfcmf/OpenWeatherMap/Util/Temperature.php b/cmfcmf/OpenWeatherMap/Util/Temperature.php new file mode 100644 index 0000000..e7c6d89 --- /dev/null +++ b/cmfcmf/OpenWeatherMap/Util/Temperature.php @@ -0,0 +1,58 @@ +now = $now; + $this->min = $min; + $this->max = $max; + } + + public function __toString() + { + return $this->now->__toString(); + } + + public function getUnit() + { + return $this->now->getUnit(); + } + + public function getValue() + { + return $this->now->getValue(); + } + + public function getDescription() + { + return $this->now->getDescription(); + } + + public function getFormatted() + { + return $this->now->getFormatted(); + } +} diff --git a/cmfcmf/OpenWeatherMap/Util/Unit.php b/cmfcmf/OpenWeatherMap/Util/Unit.php new file mode 100644 index 0000000..c0bb734 --- /dev/null +++ b/cmfcmf/OpenWeatherMap/Util/Unit.php @@ -0,0 +1,73 @@ +value = (float)$value; + $this->unit = (string)$unit; + $this->description = (string)$description; + } + + public function __toString() + { + return $this->getFormatted(); + } + + public function getUnit() + { + // Units are inconsistent. Only celsius and fahrenheit are not abbreviated. This check fixes that. + if ($this->unit == 'celsius') { + return '°C'; + } else if ($this->unit == 'fahrenheit') { + return 'F'; + } else { + return $this->unit; + } + } + + public function getValue() + { + return $this->value; + } + + public function getDescription() + { + return $this->description; + } + + public function getFormatted() + { + if ($this->getUnit() != "") { + return "{$this->getValue()} {$this->getUnit()}"; + } else { + return "{$this->getValue()}"; + } + } +} + diff --git a/cmfcmf/OpenWeatherMap/Util/Weather.php b/cmfcmf/OpenWeatherMap/Util/Weather.php new file mode 100644 index 0000000..42abda3 --- /dev/null +++ b/cmfcmf/OpenWeatherMap/Util/Weather.php @@ -0,0 +1,45 @@ +id = (int)$id; + $this->description = (string)$description; + $this->icon = (string)$icon; + } + + public function __toString() + { + return $this->description; + } + + public function getIconUrl() + { + return str_replace("%s", $this->icon, $this->iconUrl); + } +} diff --git a/cmfcmf/OpenWeatherMap/Util/Wind.php b/cmfcmf/OpenWeatherMap/Util/Wind.php new file mode 100644 index 0000000..84f4de5 --- /dev/null +++ b/cmfcmf/OpenWeatherMap/Util/Wind.php @@ -0,0 +1,30 @@ +speed = $speed; + $this->direction = $direction; + } +} diff --git a/cmfcmf/OpenWeatherMap/Weather.php b/cmfcmf/OpenWeatherMap/Weather.php new file mode 100644 index 0000000..a8df6d8 --- /dev/null +++ b/cmfcmf/OpenWeatherMap/Weather.php @@ -0,0 +1,109 @@ +OpenWeatherMap.org"; + + public function __construct($query, $units = 'imperial', $lang = 'en', $appid = '', $cacheClass = false, $seconds = 600) + { + // Disable default error handling of SimpleXML (Do not throw E_WARNINGs). + libxml_use_internal_errors(true); + libxml_clear_errors(); + + $owm = new OpenWeatherMap($cacheClass, $seconds); + + $answer = $owm->getRawData($query, $units, $lang, $appid, 'xml'); + if ($answer === false) { + // $query has the wrong format, throw error. + throw new \Exception('Error: $query has the wrong format. See the documentation of OpenWeatherMap::getRawData() to read about valid formats.'); + } + + try { + $xml = new \SimpleXMLElement($answer); + } catch(\Exception $e) { + // Invalid xml format. This happens in case OpenWeatherMap returns an error. + // OpenWeatherMap always uses json for errors, even if one specifies xml as format. + $error = json_decode($answer, true); + if (isset($error['message'])) { + throw new OWMException($error['message'], $error['cod']); + } else { + throw new OWMException('Unknown fatal error: OpenWeatherMap returned the following json object: ' . print_r($error)); + } + } + + $this->city = new City($xml->city['id'], $xml->city['name'], $xml->city->coord['lon'], $xml->city->coord['lat'], $xml->city->country); + $this->temperature = new Temperature(new Unit($xml->temperature['value'], $xml->temperature['unit']), new Unit($xml->temperature['min'], $xml->temperature['unit']), new Unit($xml->temperature['max'], $xml->temperature['unit'])); + $this->humidity = new Unit($xml->humidity['value'], $xml->humidity['unit']); + $this->pressure = new Unit($xml->pressure['value'], $xml->pressure['unit']); + + + // This is kind of a hack, because the units are missing in the xml document. + if ($units == 'metric') { + $windSpeedUnit = 'm/s'; + } else { + $windSpeedUnit = 'mph'; + } + $this->wind = new Wind(new Unit($xml->wind->speed['value'], $windSpeedUnit, $xml->wind->speed['name']), new Unit($xml->wind->direction['value'], $xml->wind->direction['code'], $xml->wind->direction['name'])); + + + $this->clouds = new Unit($xml->clouds['value'], null, $xml->clouds['name']); + $this->precipitation = new Unit($xml->precipitation['value'], $xml->precipitation['unit'], $xml->precipitation['mode']); + $this->sun = new Sun(new \DateTime($xml->city->sun['rise']), new \DateTime($xml->city->sun['set'])); + $this->weather = new WeatherObj($xml->weather['number'], $xml->weather['value'], $xml->weather['icon']); + $this->lastUpdate = new \DateTime($xml->lastupdate['value']); + } +} diff --git a/composer.json b/composer.json index c3bbd0b..fbd1765 100644 --- a/composer.json +++ b/composer.json @@ -18,5 +18,10 @@ }, "require": { "php": ">=5.3.0" + }, + "autoload": { + "psr-0": { + "cmfcmf\\": "" + } } } diff --git a/examplecache.php b/examplecache.php new file mode 100644 index 0000000..558a56a --- /dev/null +++ b/examplecache.php @@ -0,0 +1,24 @@ +"; + return false; + } + + public function getCached($query, $units, $lang, $mode) + { + echo "Get cache for $query $units $lang $mode …
"; + return false; + } + + public function setCached($content, $query, $units, $lang, $mode) + { + echo "Set cache for $query $units $lang $mode … ({$this->seconds} seconds)
"; + return false; + } +}