├── .travis.yml ├── README.md ├── Yr ├── Forecast.php ├── Location.php ├── TextualForecast.php ├── WeatherStation.php └── Yr.php ├── autoload.php ├── composer.json ├── examples ├── README.md ├── current_forecast.php ├── forecasts_in_range.php ├── hourly_forecasts.php ├── meta_info.php ├── periodic_forecasts.php ├── textual.php └── weather_stations.php ├── phpunit.xml ├── scripts ├── download_symbols.php └── symbols │ └── dummy ├── test.php └── tests └── Yr ├── ForecastTest.php ├── LocationTest.php ├── TextualForecastTest.php ├── WeatherStationTest.php └── YrTest.php /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | php: 3 | - 5.5 4 | - 5.4 5 | - 5.3 6 | - hhvm -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.org/eigan/yr-php-library.png?branch=master)](https://travis-ci.org/eigan/yr-php-library) 2 | 3 | Yr.no PHP library 4 | ================== 5 | PHP library for the norwegian wheather service yr.no. Currently in use on a fairly large norwegian site. 6 | 7 | ### Status 8 | Please note that I am not adding more feature at this time (other than accepting pull requests). Consider using something like forecast.io which has lots more data. 9 | 10 | ### Requirements 11 | - PHP 5.3 12 | 13 | ## Installation 14 | #### With [composer](https://getcomposer.org/) 15 | composer require eigan/yr 16 | #### Without composer 17 | Clone, or [download the repo as zip](https://github.com/eigan/yr-php-library/archive/master.zip). Place it somewhere on your server. Then include the autoload.php file in your code. 18 | 19 | Remember to set `date.timezone = "Europe/Oslo"` in php.ini or whatever is your timezone. 20 | 21 | ## Changelog 22 | **09 march 2014** 23 | - ! Yr::create() now returns a Location object 24 | - ! removed getLocation, use getName(), getCountry(), getType() instead 25 | - ! Forecast methods will no longer return the array when specifying null as parameter. Try to use toArray() instead 26 | - Added autoload.php to make it easier to load classes if/when structure changes. 27 | - Added Forecast->toArray() 28 | - Added Location->toArray() 29 | - Added WeatherStations (observations) 30 | - Added TextualForecasts 31 | - Added Location->getForecastAt($time), get a forecast at a specific unixtime 32 | - Added tests. 98.15% code coverage (coverage cant hit when yr return HTTP 500) 33 | - More exceptions and error checks in all classes 34 | - phpdoc cleanup 35 | 36 | ## Examples 37 | These examples require you to already have included autoload.php (or using an autoloader) 38 | 39 | #### Current forecast 40 | ```php 41 | $yr = Yr\Yr::create("Norway/Vestfold/Sandefjord/Sandefjord", "/tmp"); 42 | 43 | $forecast = $yr->getCurrentForecast(); 44 | printf("Time: %s to %s\n", $forecast->getFrom()->format("H:i"), $forecast->getTo()->format("H:i")); 45 | printf("Temp: %s %s\n", $forecast->getTemperature(), $forecast->getTemperature('unit')); 46 | printf("Wind: %smps %s\n", $forecast->getWindSpeed(), $forecast->getWindDirection('name')); 47 | ``` 48 | ``` 49 | Time: 16:00 to 17:00 50 | Temp: 8 celsius 51 | Wind: 5.7mps South-southwest 52 | ``` 53 | 54 | #### Forecasts tomorrow 55 | ```php 56 | $yr = Yr\Yr::create("Norway/Vestfold/Sandefjord/Sandefjord", "/tmp"); 57 | 58 | foreach($yr->getPeriodicForecasts(strtotime("tomorrow"), strtotime("midnight second day") - 1/*sec*/) as $forecast) { 59 | printf("Time: %s, %s degrees\n", $forecast->getFrom()->format("H:i"), $forecast->getTemperature()); 60 | } 61 | ``` 62 | ``` 63 | Time: 00:00, 5 degrees 64 | Time: 06:00, 2 degrees 65 | Time: 12:00, 8 degrees 66 | Time: 18:00, 7 degrees 67 | ``` 68 | 69 | #### Hourly forecasts rest of the day 70 | 71 | ```php 72 | $yr = Yr\Yr::create("Norway/Vestfold/Sandefjord/Sandefjord", "/tmp"); 73 | 74 | foreach($yr->getHourlyForecasts(strtotime("now"), strtotime("tomorrow") - 1/*sec*/) as $forecast) { 75 | printf("Time: %s, %s degrees\n", $forecast->getFrom()->format("H:i"), $forecast->getTemperature()); 76 | } 77 | ``` 78 | ``` 79 | Time: 20:00: 5 degrees 80 | Time: 21:00: 4 degrees 81 | Time: 22:00: 3 degrees 82 | Time: 23:00: 3 degrees 83 | ``` 84 | 85 | See more examples in [examples directory](https://github.com/eigan/yr-php-library/tree/master/examples). 86 | 87 | ## Documentation 88 | ### Yr 89 | ```php 90 | /** 91 | * 92 | * Please read the rules for using the yr api http://om.yr.no/verdata/vilkar/ 93 | * This class will implement caching for you 94 | * 95 | * @see http://om.yr.no/verdata/free-weather-data/ 96 | */ 97 | class Yr { 98 | 99 | /** 100 | * This method builds the Yr object from the freely available Yr api 101 | * 102 | * Notice that you have to be very specific about the location. Use the same location as you will find 103 | * on the yr.no site. For instance: 104 | * 105 | * This is the URL for the town Sandefjord: 106 | * http://www.yr.no/place/Norway/Vestfold/Sandefjord/Sandefjord/ 107 | * Which with this library would be: 108 | * Yr::create("Norway/Vestfold/Sandefjord/Sandefjord") 109 | * 110 | * 111 | * @todo Check data we are setting on the yr object (meta data, dates, etc) 112 | * @param String $location the location, like Norway/Vestfold/Sandefjord 113 | * @param String $cache_path where to store the cache 114 | * @param int $cache_life life of the cache 115 | * @param String $language language, norwegian or english 116 | * @return Yr\Location 117 | * @throws \RuntimeException if cache path is not writeable 118 | * @throws \RuntimeException if the location is not correct 119 | * @throws \InvalidArgumentException 120 | */ 121 | public static function create($location, $cache_path, $cache_life = 10, $language = "english"); 122 | } 123 | ``` 124 | 125 | ### Location 126 | ```php 127 | class Location { 128 | 129 | /** 130 | * @return String 131 | */ 132 | public function getName(); 133 | 134 | /** 135 | * @return String 136 | */ 137 | public function getType(); 138 | 139 | /** 140 | * @return String 141 | */ 142 | public function getCountry(); 143 | /** 144 | * @return String 145 | */ 146 | public function getTimezone(); 147 | 148 | /** 149 | * @return array 150 | */ 151 | public function getLatLong(); 152 | 153 | /** 154 | * List of links to the yr.no frontend 155 | * 156 | * @return array 157 | */ 158 | public function getLinks(); 159 | 160 | /** 161 | * Returns the current forecast (using periodic) 162 | * 163 | * @return Forecast 164 | */ 165 | public function getCurrentForecast(); 166 | 167 | /** 168 | * Returns the upcoming forecasts as array of Forecast objects 169 | * 170 | * You can optionally specify $from and $to as unixtime. 171 | * 172 | * @param int $from unixtime for when the first forecast should start 173 | * @param int $to unixtime for when the last forecast should start 174 | * @return array Forecast objects 175 | */ 176 | public function getHourlyForecasts($from = null, $to = null); 177 | 178 | /** 179 | * There is 4 peridos in a day. You can check the Forecast::getPeriod() 180 | * 181 | * You can optionally specify $from and $to as unixtime. 182 | * 183 | * @param int $from unixtime for when the first forecast should start 184 | * @param int $to unixtime for when the last forecast should start 185 | * @return array Forecast objects 186 | */ 187 | public function getPeriodicForecasts($from = null, $to = null); 188 | 189 | /** 190 | * Get a Forecast at a specific time 191 | * 192 | * @param String $time unixtime for when the forecast should be 193 | * @return Forecast[] 194 | */ 195 | public function getForecastAt($time); 196 | 197 | /** 198 | * Note: Th textual forecasts is always norwegian.. 199 | * @return TextualForecast[] 200 | */ 201 | public function getTextualForecasts(); 202 | 203 | /** 204 | * @return WeatherStation[] 205 | */ 206 | public function getWeatherStations(); 207 | 208 | /** 209 | * @return \Datetime 210 | */ 211 | public function getSunrise(); 212 | 213 | /** 214 | * @return \Datetime 215 | */ 216 | public function getSunset(); 217 | /** 218 | * Returns the time the hourly data was last updated 219 | * @return \DateTime 220 | */ 221 | public function getLastUpdated(); 222 | 223 | /** 224 | * Returns the time this will update next time the hourly data will update 225 | * @return \DateTime 226 | */ 227 | public function getNextUpdate(); 228 | /** 229 | * @return String 230 | */ 231 | public function getCreditUrl(); 232 | 233 | /** 234 | * You have to display this text with a link to the creditUrl! Read rules 235 | * @see getCreditUrl() 236 | * @return String 237 | */ 238 | public function getCreditText() 239 | { 240 | return $this->credit_text; 241 | } 242 | 243 | /** 244 | * @return array 245 | */ 246 | public function toArray(); 247 | } 248 | ``` 249 | 250 | ### Forecast 251 | ```php 252 | /** 253 | * Representing a forecast for the yr service 254 | * 255 | */ 256 | class Forecast { 257 | 258 | /** 259 | * The symbol have three attributes with value 260 | * number [default] 261 | * name 262 | * var 263 | * 264 | * Default value will give "number" 265 | * 266 | * @param String $key number|name|var 267 | * @return string|array default is name 268 | */ 269 | public function getSymbol($key = "name"); 270 | 271 | /** 272 | * The symbol can have three attributes with value 273 | * value [default] 274 | * minvalue 275 | * maxvalue 276 | * 277 | * Default value will give "value" 278 | * 279 | * @param String $key value|minvalue|maxvalue 280 | * @return string 281 | */ 282 | public function getPrecipitation($key = "value"); 283 | 284 | 285 | /** 286 | * The wind direction have three attributes with value 287 | * deg 288 | * code [default] 289 | * name 290 | * 291 | * Default value will send the code 292 | * 293 | * @param String $key deg|code|name 294 | * @return string|array default is code 295 | */ 296 | public function getWindDirection($key = "code"); 297 | 298 | 299 | /** 300 | * The wind speed have two attributes with value 301 | * mps [default] 302 | * name 303 | * 304 | * @param String $key mps|name 305 | * @return string|array default value is meters pr sec 306 | */ 307 | public function getWindSpeed($key = "mps"); 308 | 309 | /** 310 | * Utility method to get the filename for the arrows (speed and direction) 311 | * 312 | * http://fil.nrk.no/yr/grafikk/vindpiler/32/vindpil.{$speed}.{$degree}.png 313 | * 314 | * So you can use the icon like so: 315 | * http://fil.nrk.no/yr/grafikk/vindpiler/32/vindpil.{$forecast->getWindIconKey()}.png 316 | * 317 | * if it returns 0, then it should be "vindstille" (no wind) http://fil.nrk.no/yr/grafikk/vindpiler/32/vindstille.png 318 | * 319 | * @returm string 320 | */ 321 | public function getWindIconKey(); 322 | 323 | /** 324 | * The temperatur have two attributes with value 325 | * unit 326 | * value [default] 327 | * 328 | * @param String $key value|unit 329 | * @return string|array see documentation 330 | */ 331 | public function getTemperature($key = "value"); 332 | 333 | /** 334 | * The pressure have two attributes with value 335 | * unit 336 | * value [default] 337 | * 338 | * @param String $key value|unit 339 | * @return string|array see documentation 340 | */ 341 | public function getPressure($key = "value"); 342 | 343 | 344 | /** 345 | * Time from when the forecast begins 346 | * @return \DateTime 347 | */ 348 | public function getFrom(); 349 | 350 | /** 351 | * Time for when the forecast ends 352 | * @return \DateTime 353 | */ 354 | public function getTo(); 355 | 356 | /** 357 | * Period of the day. This might be null in hourly 358 | * @return int|null 359 | */ 360 | public function getPeriod(); 361 | 362 | /** 363 | * @return array 364 | */ 365 | public function toArray(); 366 | } 367 | ``` 368 | 369 | ### TextualForecast 370 | ```php 371 | /** 372 | * Textual forecasts for a given day (or two days) 373 | * Note: There is some html inside the getText()... 374 | * 375 | */ 376 | class TextualForecast { 377 | 378 | /** 379 | * @return String 380 | */ 381 | public function getTitle(); 382 | 383 | /** 384 | * @return String 385 | */ 386 | public function getText(); 387 | 388 | /** 389 | * @return \Datetime 390 | */ 391 | public function getFrom(); 392 | 393 | /** 394 | * @return \DateTime 395 | */ 396 | public function getTo(); 397 | } 398 | ``` 399 | 400 | ### WeatherStation 401 | ```php 402 | /** 403 | * Weather Station 404 | * Note that the Forecast object will not be complete since all data from it might not be set :/ 405 | * 406 | */ 407 | class WeatherStation { 408 | 409 | /** 410 | * @return String 411 | */ 412 | public function getName(); 413 | 414 | /** 415 | * @return numeric 416 | */ 417 | public function getDistance(); 418 | 419 | /** 420 | * array('lat' => '[xx.xxxx]', 'long' => '[xx.xxxx]') 421 | * 422 | * @return array 423 | */ 424 | public function getLatLong(); 425 | 426 | /** 427 | * 428 | * @return String 429 | */ 430 | public function getSource(); 431 | 432 | /** 433 | * Warning: Not everything will be set on this object 434 | * 435 | * @return Forecast the current forecast reported by this weatherstation 436 | */ 437 | public function getForecast(); 438 | 439 | /** 440 | * @return array 441 | */ 442 | public function toArray(); 443 | } 444 | ``` 445 | 446 | ## License 447 | MIT http://www.tldrlegal.com/license/mit-license 448 | -------------------------------------------------------------------------------- /Yr/Forecast.php: -------------------------------------------------------------------------------- 1 | 10 | */ 11 | class Forecast 12 | { 13 | /** 14 | * From datetime. 15 | * 16 | * @var \DateTime 17 | */ 18 | public $from; 19 | 20 | /** 21 | * To datetime. 22 | * 23 | * @var \DateTime 24 | */ 25 | public $to; 26 | 27 | /** 28 | * Period of the date, will be a number from 0 to 4 29 | * where 0 is early in the morning and 4 is at night. 30 | * 31 | * @var int 32 | */ 33 | public $period; 34 | 35 | /** 36 | * Typically the icon to display. 37 | * 38 | * @var array 39 | */ 40 | public $symbol; 41 | 42 | /** 43 | * Precipitation in millimeters. 44 | * 45 | * @var String 46 | */ 47 | public $precipitation; 48 | 49 | /** 50 | * Data for wind direction. 51 | * 52 | * @var array 53 | */ 54 | public $wind_direction; 55 | 56 | /** 57 | * Data for wind speed. 58 | * 59 | * @var array 60 | */ 61 | public $wind_speed; 62 | 63 | /** 64 | * Data for temperature. 65 | * 66 | * @var array 67 | */ 68 | public $temperature; 69 | 70 | /** 71 | * Data for pressure. 72 | * 73 | * @var array 74 | */ 75 | public $pressure; 76 | 77 | /** 78 | * We do NOTHING. 79 | */ 80 | public function __construct() 81 | { 82 | } 83 | 84 | /** 85 | * Creates from simplexml object. 86 | * 87 | * @param \SimpleXMLElement $xml The xml node element 88 | * 89 | * @return Forecast 90 | * 91 | * @throws \RuntimeException If some data is missing for xml 92 | */ 93 | public static function getForecastFromXml(\SimpleXMLElement $xml) 94 | { 95 | $forecast = new Forecast(); 96 | 97 | $data = Yr::xmlToArray($xml); 98 | 99 | if (!isset($data['from'], $data['to'])) { 100 | throw new \RuntimeException("Missing from/to for forecast"); 101 | } 102 | 103 | $forecast->from = \DateTime::createFromFormat(Yr::XML_DATE_FORMAT, $data['from']); 104 | $forecast->to = \DateTime::createFromFormat(Yr::XML_DATE_FORMAT, $data['to']); 105 | $forecast->period = isset($data['period']) ? $data['period'] : ""; 106 | 107 | if (!isset($data['symbol'], 108 | $data['precipitation'], 109 | $data['windDirection'], 110 | $data['windSpeed'], 111 | $data['temperature'], 112 | $data['pressure'])) { 113 | throw new \RuntimeException("Missing data for forecast"); 114 | } 115 | 116 | $forecast->symbol = $data['symbol']; 117 | $forecast->precipitation = $data['precipitation']; 118 | $forecast->wind_direction = $data['windDirection']; 119 | $forecast->wind_speed = $data['windSpeed']; 120 | $forecast->temperature = $data['temperature']; 121 | $forecast->pressure = $data['pressure']; 122 | 123 | return $forecast; 124 | } 125 | 126 | /** 127 | * The symbol have four attributes with value 128 | * number 129 | * numberEx 130 | * name [default] 131 | * var. 132 | * 133 | * Default value will give "name" 134 | * 135 | * @param String $key number|name|var 136 | * 137 | * @return string|array default is name 138 | */ 139 | public function getSymbol($key = "name") 140 | { 141 | return isset($this->symbol[$key]) ? $this->symbol[$key] : null; 142 | } 143 | 144 | /** 145 | * @param array $symbol 146 | */ 147 | public function setSymbol($symbol) 148 | { 149 | $this->symbol = $symbol; 150 | } 151 | 152 | /** 153 | * The symbol can have three attributes with value 154 | * value [default] 155 | * minvalue 156 | * maxvalue. 157 | * 158 | * Default value will give "value" 159 | * 160 | * @param String $key value|minvalue|maxvalue 161 | * 162 | * @return string 163 | */ 164 | public function getPrecipitation($key = "value") 165 | { 166 | return isset($this->precipitation[$key]) ? $this->precipitation[$key] : null; 167 | } 168 | 169 | /** 170 | * @param String $precipitation 171 | */ 172 | public function setPrecipitation($precipitation) 173 | { 174 | $this->precipitation = $precipitation; 175 | } 176 | 177 | /** 178 | * The wind direction have three attributes with value 179 | * deg 180 | * code [default] 181 | * name. 182 | * 183 | * Default value will send the code 184 | * 185 | * @param String $key deg|code|name 186 | * 187 | * @return string|array default is code 188 | */ 189 | public function getWindDirection($key = "code") 190 | { 191 | return isset($this->wind_direction[$key]) ? $this->wind_direction[$key] : null; 192 | } 193 | 194 | /** 195 | * @param array $wind_direction 196 | */ 197 | public function setWindDirection($wind_direction) 198 | { 199 | $this->wind_direction = $wind_direction; 200 | } 201 | 202 | /** 203 | * The wind speed have two attributes with value 204 | * mps [default] 205 | * name. 206 | * 207 | * @param String $key mps|name 208 | * 209 | * @return string|array default value is meters pr sec 210 | */ 211 | public function getWindSpeed($key = "mps") 212 | { 213 | return isset($this->wind_speed[$key]) ? $this->wind_speed[$key] : null; 214 | } 215 | 216 | /** 217 | * @param array $wind_speed 218 | */ 219 | public function setWindSpeed($wind_speed) 220 | { 221 | $this->wind_speed = $wind_speed; 222 | } 223 | 224 | /** 225 | * Utility method to get the filename for the arrows (speed and direction). 226 | * 227 | * http://fil.nrk.no/yr/grafikk/vindpiler/32/vindpil.{$speed}.{$degree}.png 228 | * 229 | * So you can use the icon like so: 230 | * http://fil.nrk.no/yr/grafikk/vindpiler/32/vindpil.{$forecast->getWindIconKey()}.png 231 | * 232 | * if it returns 0, then it should be "vindstille" (no wind) http://fil.nrk.no/yr/grafikk/vindpiler/32/vindstille.png 233 | * 234 | * @returm string 235 | */ 236 | public function getWindIconKey() 237 | { 238 | $speed = (round(($this->getWindSpeed("mps")/2.5)) * 2.5) * 10; 239 | $speed = str_pad($speed, 4, '0', STR_PAD_LEFT); 240 | 241 | // 2 and down is 0 speed - vindstille 242 | if ($this->getWindSpeed() <= 0.2) { 243 | return 0; 244 | } 245 | 246 | $degree = round((($this->getWindDirection("deg")/10) * 2) / 2) * 10; 247 | $degree = str_pad($degree, 3, '0', STR_PAD_LEFT); 248 | 249 | // 360 degree is 0 250 | if ($degree >= 360) { 251 | $degree = 0; 252 | } 253 | 254 | return "$speed.$degree"; 255 | } 256 | 257 | /** 258 | * The temperatur have two attributes with value 259 | * unit 260 | * value [default]. 261 | * 262 | * @param String $key value|unit 263 | * 264 | * @return string|array see documentation 265 | */ 266 | public function getTemperature($key = "value") 267 | { 268 | return isset($this->temperature[$key]) ? $this->temperature[$key] : null; 269 | } 270 | 271 | /** 272 | * @param array $temperature 273 | */ 274 | public function setTemperature($temperature) 275 | { 276 | $this->temperature = $temperature; 277 | } 278 | 279 | /** 280 | * The pressure have two attributes with value 281 | * unit 282 | * value [default]. 283 | * 284 | * @param String $key value|unit 285 | * 286 | * @return string|array see documentation 287 | */ 288 | public function getPressure($key = "value") 289 | { 290 | return isset($this->pressure[$key]) ? $this->pressure[$key] : null; 291 | } 292 | 293 | /** 294 | * @param array $pressure 295 | */ 296 | public function setPressure($pressure) 297 | { 298 | $this->pressure = $pressure; 299 | } 300 | 301 | /** 302 | * Time from when the forecast begins. 303 | * 304 | * @return \DateTime 305 | */ 306 | public function getFrom() 307 | { 308 | return $this->from; 309 | } 310 | 311 | /** 312 | * @param \DateTime $from 313 | */ 314 | public function setFrom(\DateTime $from) 315 | { 316 | $this->from = $from; 317 | } 318 | 319 | /** 320 | * Time for when the forecast ends. 321 | * 322 | * @return \DateTime 323 | */ 324 | public function getTo() 325 | { 326 | return $this->to; 327 | } 328 | 329 | /** 330 | * @param \DateTime $to 331 | */ 332 | public function setTo(\DateTime $to) 333 | { 334 | $this->to = $to; 335 | } 336 | 337 | /** 338 | * Period of the day. This might be null in hourly. 339 | * 340 | * @return int|null 341 | */ 342 | public function getPeriod() 343 | { 344 | return strlen($this->period) > 0 ? $this->period : null; 345 | } 346 | 347 | /** 348 | * @param int $period 349 | */ 350 | public function setPeriod($period) 351 | { 352 | $this->period = $period; 353 | } 354 | 355 | /** 356 | * @return array 357 | */ 358 | public function toArray() 359 | { 360 | return array( 361 | 'from' => $this->getFrom(), 362 | 'to' => $this->getTo(), 363 | 'symbol' => $this->symbol, 364 | 'temperature' => $this->temperature, 365 | 'wind_speed' => $this->wind_speed, 366 | 'wind_direction' => $this->wind_direction, 367 | 'period' => $this->period, 368 | ); 369 | } 370 | } 371 | -------------------------------------------------------------------------------- /Yr/Location.php: -------------------------------------------------------------------------------- 1 | location = $location; 84 | $this->forecasts_periodic = $forecasts_periodic; 85 | $this->forecasts_hourly = $forecasts_hourly; 86 | $this->links = array(); 87 | $this->weather_stations = array(); 88 | } 89 | 90 | /** 91 | * @return String 92 | */ 93 | public function getName() 94 | { 95 | return $this->location['name']; 96 | } 97 | 98 | /** 99 | * @return String 100 | */ 101 | public function getType() 102 | { 103 | return $this->location['type']; 104 | } 105 | 106 | /** 107 | * @return String 108 | */ 109 | public function getCountry() 110 | { 111 | return $this->location['country']; 112 | } 113 | 114 | /** 115 | * @return String 116 | */ 117 | public function getTimezone() 118 | { 119 | return $this->location['timezone']['id']; 120 | } 121 | 122 | /** 123 | * @return array 124 | */ 125 | public function getLatLong() 126 | { 127 | return array( 128 | 'lat' => $this->location['location']['latitude'], 129 | 'long' => $this->location['location']['longitude'], 130 | ); 131 | } 132 | 133 | /** 134 | * List of links to the yr.no frontend 135 | * 136 | * @return array 137 | */ 138 | public function getLinks() 139 | { 140 | return $this->links; 141 | } 142 | 143 | /** 144 | * Adds a link 145 | * 146 | * @param String $name 147 | * @param String $url 148 | */ 149 | public function addLink($name, $url) 150 | { 151 | $this->links[$name] = $url; 152 | } 153 | 154 | /** 155 | * Returns the current forecast (using periodic) 156 | * 157 | * @return Forecast 158 | */ 159 | public function getCurrentForecast() 160 | { 161 | $forecast = reset($this->forecasts_hourly); 162 | 163 | return $forecast instanceof Forecast ? $forecast : null; 164 | } 165 | 166 | /** 167 | * Returns the upcoming forecasts as array of Forecast objects 168 | * 169 | * You can optionally specify $from and $to as unixtime. 170 | * 171 | * @param int $from unixtime for when the first forecast should start 172 | * @param int $to unixtime for when the last forecast should start 173 | * @return array Forecast objects 174 | */ 175 | public function getHourlyForecasts($from = null, $to = null) 176 | { 177 | if (!is_null($from) || !is_null($to)) { 178 | return $this->getForecastsBetweenTime($this->forecasts_hourly, $from, $to); 179 | } 180 | 181 | return $this->forecasts_hourly; 182 | } 183 | 184 | /** 185 | * There is 4 peridos in a day. You can check the Forecast::getPeriod() 186 | * 187 | * You can optionally specify $from and $to as unixtime. 188 | * 189 | * @param int $from unixtime for when the first forecast should start 190 | * @param int $to unixtime for when the last forecast should start 191 | * @return array Forecast objects 192 | */ 193 | public function getPeriodicForecasts($from = null, $to = null) 194 | { 195 | if (!is_null($from) || !is_null($to)) { 196 | return $this->getForecastsBetweenTime($this->forecasts_periodic, $from, $to); 197 | } 198 | 199 | return $this->forecasts_periodic; 200 | } 201 | 202 | /** 203 | * Get a Forecast at a specific time 204 | * 205 | * @param String $time unixtime for when the forecast should be 206 | * @return Forecast[] 207 | */ 208 | public function getForecastAt($time) 209 | { 210 | $forecasts = $this->getForecastsBetweenTime($this->forecasts_hourly, $time); 211 | 212 | return reset($forecasts); 213 | } 214 | 215 | /** 216 | * Internal function to find forecasts between a given time 217 | * 218 | * Notice that if $from is null, we change it to now() 219 | * and if $to is null, we change it to the time one year from now 220 | * 221 | * @param Forecast[] $forecasts the list of forecasts to check 222 | * @param int $from unixtime for when the forecast should start 223 | * @param int $to unixtime for when the last forecast should start 224 | * @return array list of matching forecasts 225 | */ 226 | protected function getForecastsBetweenTime($forecasts, $from, $to = null) 227 | { 228 | $result = array(); 229 | 230 | // Check for null, or non valid unixtimes 231 | $from = is_null($from) || !is_int($from) ? time() : $from; 232 | $to = is_null($to) || !is_int($to) ? strtotime("1 year") : $to; 233 | 234 | foreach ($forecasts as $forecast) { 235 | if ($forecast->getFrom()->getTimestamp() >= $from && 236 | $forecast->getFrom()->getTimestamp() <= $to) { 237 | $result[] = $forecast; 238 | } 239 | } 240 | 241 | return $result; 242 | } 243 | 244 | /** 245 | * Note: The textual forecasts is always norwegian.. 246 | * Note: Places outside of Norway might not have textual forecasts 247 | * 248 | * @return TextualForecast[] 249 | */ 250 | public function getTextualForecasts() 251 | { 252 | return $this->textual_forecasts; 253 | } 254 | 255 | /** 256 | * @param TextualForecast[] $forecasts 257 | */ 258 | public function setTextualForecasts(array $forecasts) 259 | { 260 | $this->textual_forecasts = $forecasts; 261 | } 262 | 263 | /** 264 | * Note: Places outside of Norway might not have weather stations 265 | * 266 | * @return WeatherStation[] 267 | */ 268 | public function getWeatherStations() 269 | { 270 | return $this->weather_stations; 271 | } 272 | 273 | /** 274 | * @param WeatherStation[] $weather_stations 275 | */ 276 | public function setWeatherStations($weather_stations) 277 | { 278 | $this->weather_stations = $weather_stations; 279 | } 280 | 281 | /** 282 | * @return \Datetime 283 | */ 284 | public function getSunrise() 285 | { 286 | return $this->sunrise; 287 | } 288 | 289 | /** 290 | * @param \Datetime 291 | */ 292 | public function setSunrise(\Datetime $time) 293 | { 294 | $this->sunrise = $time; 295 | } 296 | 297 | /** 298 | * @return \Datetime 299 | */ 300 | public function getSunset() 301 | { 302 | return $this->sunset; 303 | } 304 | 305 | /** 306 | * @param \Datetime $time 307 | */ 308 | public function setSunset(\Datetime $time) 309 | { 310 | $this->sunset = $time; 311 | } 312 | 313 | /** 314 | * Returns the time the hourly data was last updated 315 | * @return \DateTime 316 | */ 317 | public function getLastUpdated() 318 | { 319 | return $this->last_update_date; 320 | } 321 | 322 | /** 323 | * Setter for last update 324 | * @param \DateTime $date 325 | */ 326 | public function setLastUpdated(\Datetime $date) 327 | { 328 | $this->last_update_date = $date; 329 | } 330 | 331 | /** 332 | * Returns the time this will update next time the hourly data will update 333 | * @return \DateTime 334 | */ 335 | public function getNextUpdate() 336 | { 337 | return $this->next_update_date; 338 | } 339 | 340 | /** 341 | * 342 | * @param \DateTime 343 | */ 344 | public function setNextUpdate(\Datetime $date) 345 | { 346 | $this->next_update_date = $date; 347 | } 348 | 349 | /** 350 | * @return String 351 | */ 352 | public function getCreditUrl() 353 | { 354 | return $this->credit_url; 355 | } 356 | 357 | /** 358 | * @param String $url 359 | */ 360 | public function setCreditUrl($url) 361 | { 362 | $this->credit_url = $url; 363 | } 364 | 365 | /** 366 | * You have to display this text with a link to the creditUrl! Read rules 367 | * @see getCreditUrl() 368 | * @return String 369 | */ 370 | public function getCreditText() 371 | { 372 | return $this->credit_text; 373 | } 374 | 375 | /** 376 | * @param String $text 377 | */ 378 | public function setCreditText($text) 379 | { 380 | $this->credit_text = $text; 381 | } 382 | 383 | /** 384 | * @return array 385 | */ 386 | public function toArray() 387 | { 388 | return array( 389 | 'location' => $this->location, 390 | 'links' => $this->links, 391 | 'last_update' => $this->getLastUpdated(), 392 | 'next_update' => $this->getNextUpdate(), 393 | 'credit_text' => $this->getCreditText(), 394 | 'credit_url' => $this->getCreditUrl(), 395 | 'sunrise' => $this->getSunrise(), 396 | 'sunset' => $this->getSunset(), 397 | 'forecasts' => $this->getHourlyForecasts(), 398 | 'weather_stations' => $this->getWeatherStations(), 399 | ); 400 | } 401 | } 402 | -------------------------------------------------------------------------------- /Yr/TextualForecast.php: -------------------------------------------------------------------------------- 1 | 12 | */ 13 | class TextualForecast 14 | { 15 | /** 16 | * @var String 17 | */ 18 | protected $title; 19 | 20 | /** 21 | * @var String 22 | */ 23 | protected $text; 24 | 25 | /** 26 | * @var \Datetime 27 | */ 28 | protected $from; 29 | 30 | /** 31 | * @var \Datetime 32 | */ 33 | protected $to; 34 | 35 | /** 36 | * @var String 37 | */ 38 | const XML_DATE_FORMAT = "Y-m-d"; 39 | 40 | /** 41 | * @param String $title 42 | * @param String $text 43 | * @param \DateTime $from 44 | * @param \DateTime $to 45 | * @throws \InvalidArgumentException 46 | */ 47 | public function __construct($title, $text, \DateTime $from, \DateTime $to = null) 48 | { 49 | if (empty($title) || empty($text)) { 50 | throw new \InvalidArgumentException("Title/or text is empty"); 51 | } 52 | 53 | $this->title = $title; 54 | $this->text = $text; 55 | $this->from = $from; 56 | $this->to = $to === null ? $from : $to; 57 | } 58 | 59 | /** 60 | * @param \SimpleXMLElement $xml 61 | * @return TextualForecast 62 | * @throws \InvalidArgumentException 63 | */ 64 | public static function createTextualForecastFromXml(\SimpleXMLElement $xml) 65 | { 66 | $data = Yr::xmlToArray($xml); 67 | 68 | $title = $data['title']; 69 | $text = $data['body']; 70 | $from = \DateTime::createFromFormat(TextualForecast::XML_DATE_FORMAT, $data['from']); 71 | $to = \DateTime::createFromFormat(TextualForecast::XML_DATE_FORMAT, $data['to']); 72 | 73 | return new TextualForecast($title, $text, $from, $to); 74 | } 75 | 76 | /** 77 | * @return String 78 | */ 79 | public function getTitle() 80 | { 81 | return $this->title; 82 | } 83 | 84 | /** 85 | * @return String 86 | */ 87 | public function getText() 88 | { 89 | return $this->text; 90 | } 91 | 92 | /** 93 | * @return \Datetime 94 | */ 95 | public function getFrom() 96 | { 97 | return $this->from; 98 | } 99 | 100 | /** 101 | * @return \DateTime 102 | */ 103 | public function getTo() 104 | { 105 | return $this->to; 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /Yr/WeatherStation.php: -------------------------------------------------------------------------------- 1 | 11 | */ 12 | class WeatherStation 13 | { 14 | /** 15 | * @var String 16 | */ 17 | protected $name; 18 | 19 | /** 20 | * @var int 21 | */ 22 | protected $distance; 23 | 24 | /** 25 | * @var array 26 | */ 27 | protected $latLong; 28 | 29 | /** 30 | * @var String 31 | */ 32 | protected $source; 33 | 34 | /** 35 | * @var Forecast 36 | */ 37 | protected $forecast; 38 | 39 | /** 40 | * @param $name 41 | * @param $distance 42 | * @param array $latLong 43 | * @param $source 44 | */ 45 | public function __construct($name, $distance, array $latLong, $source) 46 | { 47 | $this->name = $name; 48 | $this->distance = (int) $distance; 49 | $this->latLong = $latLong; 50 | $this->source = $source; 51 | 52 | $this->forecast = new Forecast(); 53 | } 54 | 55 | /** 56 | * @param \SimpleXMLElement $xml 57 | * @return WeatherStation 58 | */ 59 | public static function getWeatherStationFromXml(\SimpleXMLElement $xml) 60 | { 61 | $data = Yr::xmlToArray($xml); 62 | 63 | $name = $data['name']; 64 | $distance = $data['distance']; 65 | $latLong = array('lat' => $data['lat'], 'long' => $data['lon']); 66 | $source = $data['source']; 67 | 68 | $station = new WeatherStation($name, $distance, $latLong, $source); 69 | 70 | $forecast = $station->getForecast(); 71 | 72 | if (isset($data['symbol'])) { 73 | $forecast->setSymbol($data['symbol']); 74 | } 75 | 76 | if (isset($data['temperature'])) { 77 | $forecast->setTemperature($data['temperature']); 78 | } 79 | 80 | if (isset($data['windDirection'])) { 81 | $forecast->setWindDirection($data['windDirection']); 82 | } 83 | 84 | if (isset($data['windSpeed'])) { 85 | $forecast->setWindSpeed($data['windSpeed']); 86 | } 87 | 88 | return $station; 89 | } 90 | 91 | /** 92 | * @return String 93 | */ 94 | public function getName() 95 | { 96 | return $this->name; 97 | } 98 | 99 | /** 100 | * @return int 101 | */ 102 | public function getDistance() 103 | { 104 | return $this->distance; 105 | } 106 | 107 | /** 108 | * array('lat' => '[xx.xxxx]', 'long' => '[xx.xxxx]') 109 | * 110 | * @return array 111 | */ 112 | public function getLatLong() 113 | { 114 | return $this->latLong; 115 | } 116 | 117 | /** 118 | * 119 | * @return String 120 | */ 121 | public function getSource() 122 | { 123 | return $this->source; 124 | } 125 | 126 | /** 127 | * Warning: Not everything will be set on this object 128 | * 129 | * @return Forecast the current forecast reported by this weatherstation 130 | */ 131 | public function getForecast() 132 | { 133 | return $this->forecast; 134 | } 135 | 136 | /** 137 | * @param Forecast $forecast 138 | */ 139 | public function setForecast(Forecast $forecast) 140 | { 141 | $this->forecast = $forecast; 142 | } 143 | 144 | /** 145 | * @return array 146 | */ 147 | public function toArray() 148 | { 149 | return array( 150 | 'name' => $this->name, 151 | 'distance' => $this->distance, 152 | 'latLong' => $this->latLong, 153 | 'source' => $this->source, 154 | 'forecast' => $this->forecast, 155 | ); 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /Yr/Yr.php: -------------------------------------------------------------------------------- 1 | 12 | */ 13 | class Yr 14 | { 15 | /** 16 | * This is the format used in the xml files. 17 | * It is converted to DateTime everywhere. 18 | * 19 | * @var string 20 | */ 21 | const XML_DATE_FORMAT = "Y-m-d?H:i:s"; 22 | 23 | /** 24 | * Yr url. 25 | */ 26 | const API_URL = "http://www.yr.no/"; 27 | 28 | /** 29 | * HTTP 200 response with text/xml. 30 | */ 31 | const SERVICE_OK = 1; 32 | 33 | /** 34 | * HTTP 200 with text/html, or HTTP 404. 35 | */ 36 | const SERVICE_LOCATION_INVALID = 5; 37 | 38 | /** 39 | * HTTP 500, or no response. 40 | */ 41 | const SERVICE_UNKNOWN_STATE = 10; 42 | 43 | /** 44 | * Prefix for the xml files. 45 | */ 46 | const CACHE_PREFIX = "phpyrno_"; 47 | 48 | /** 49 | * Only static functions here. 50 | */ 51 | protected function __construct() 52 | { 53 | } 54 | 55 | /** 56 | * This method downloads and builds the Yr object from the freely available Yr api. 57 | * 58 | * Notice that you have to be very specific about the location. 59 | * Use the same location as you will find on the yr.no site. For instance: 60 | * 61 | * This is the URL for the town Sandefjord: 62 | * http://www.yr.no/place/Norway/Vestfold/Sandefjord/Sandefjord/ 63 | * Which with this library would be: 64 | * Yr::create("Norway/Vestfold/Sandefjord/Sandefjord") 65 | * 66 | * @param String $location the location, like Norway/Vestfold/Sandefjord 67 | * @param String $cache_path where to store the cache 68 | * @param int $cache_life life of the cache in minutes 69 | * @param String $language language, norwegian or english 70 | * 71 | * @return Location 72 | * 73 | * @throws \RuntimeException if cache path is not writeable 74 | * @throws \RuntimeException if the location is not correct 75 | * @throws \InvalidArgumentException 76 | * 77 | * @todo Check data we are setting on the yr object (meta data, dates, etc) 78 | */ 79 | public static function create( 80 | $location, 81 | $cache_path, 82 | $cache_life = 10, 83 | $language = "english" 84 | ) { 85 | if (!isset($location) || empty($location)) { 86 | throw new \InvalidArgumentException("Location need to be set"); 87 | } 88 | 89 | if (!isset($cache_path) || empty($cache_path)) { 90 | throw new \InvalidArgumentException("Cache path need to be set"); 91 | } 92 | 93 | // Get url, it is different for each language 94 | $baseurl = self::getApiUrlByLanguage($language); 95 | 96 | // Clean the cache path 97 | $cache_path = realpath($cache_path).DIRECTORY_SEPARATOR; 98 | 99 | // Convert cache life to seconds 100 | $cache_life_sec = $cache_life * 60; 101 | 102 | // Check if cache path is readable 103 | if (!is_writable($cache_path)) { 104 | throw new \RuntimeException("Cache path ($cache_path) is not writable"); 105 | } 106 | 107 | // Cache paths for the xml data 108 | $cachename = self::CACHE_PREFIX.md5($baseurl.$location); 109 | $xml_periodic_path = $cache_path.$cachename."_periodic.xml"; 110 | $xml_hourly_path = $cache_path.$cachename."_hourly.xml"; 111 | 112 | // Check response from web service 113 | // This is a critical process if we have no cache. 114 | // Please see Yr::getUrlResponseCode() for explanation 115 | if (!is_readable($xml_periodic_path) || !is_readable($xml_hourly_path)) { 116 | $test = self::getServiceResponseCode($baseurl.$location); 117 | 118 | if ($test == self::SERVICE_LOCATION_INVALID) { 119 | throw new \RuntimeException( 120 | "The location ($location) is wrong. 121 | Please check Yr::create() documentation." 122 | ); 123 | } elseif ($test == self::SERVICE_UNKNOWN_STATE) { 124 | throw new \RuntimeException( 125 | "Could not connect to yr service. 126 | Tried the url for 7 times, but did not work. 127 | Might be do to invalid location, or yr service is down." 128 | ); 129 | } 130 | } 131 | 132 | // Download the periodic xml if we don't have it 133 | $xml_periodic = self::downloadData( 134 | "$baseurl/$location/forecast.xml", 135 | $xml_periodic_path, 136 | $cache_life_sec 137 | ); 138 | 139 | // Download the hourly xml if we don't have it 140 | $xml_hourly = self::downloadData( 141 | "$baseurl/$location/forecast_hour_by_hour.xml", 142 | $xml_hourly_path, 143 | $cache_life_sec 144 | ); 145 | 146 | return self::createFromXML($xml_periodic, $xml_hourly); 147 | } 148 | 149 | /** 150 | * Build the Yr object 151 | * 152 | * @param String $periodic XML file content 153 | * @param String $hourly XML file content 154 | * 155 | * @return Location 156 | * 157 | * @throws \InvalidArgumentException 158 | * @throws \RuntimeException 159 | */ 160 | public static function createFromXML($periodic, $hourly) 161 | { 162 | if (!isset($periodic) || empty($periodic)) { 163 | throw new \InvalidArgumentException("Periodic XML document need to be set"); 164 | } 165 | 166 | if (!isset($hourly) || empty($hourly)) { 167 | throw new \InvalidArgumentException("Hourly XML document need to be set"); 168 | } 169 | 170 | $xml_hourly = new \SimpleXMLElement($hourly); 171 | $xml_periodic = new \SimpleXMLElement($periodic); 172 | 173 | // Forecasts 174 | $forecasts_hourly = self::getForecastsFromXml($xml_hourly); 175 | $forecasts_periodic = self::getForecastsFromXml($xml_periodic); 176 | 177 | // Textual 178 | $textual_forecasts = self::getTextualForecastsFromXml($xml_hourly); 179 | 180 | // weather_stations 181 | $weather_stations = self::getWeatherStationsFromXml($xml_hourly); 182 | 183 | // Get other data for our object 184 | $location = self::xmlToArray($xml_periodic->location); 185 | $links = self::xmlToArray($xml_periodic->links); 186 | $credit = self::xmlToArray($xml_periodic->credit->link); 187 | $meta = self::xmlToArray($xml_periodic->meta); 188 | $sun = self::xmlToArray($xml_periodic->sun); 189 | 190 | // Set the data on the object 191 | try { 192 | $yr = new Location($location, $forecasts_periodic, $forecasts_hourly); 193 | 194 | $yr->setWeatherStations($weather_stations); 195 | $yr->setTextualForecasts($textual_forecasts); 196 | 197 | if (isset($links['link'])) { 198 | foreach ($links['link'] as $link) { 199 | $yr->addLink($link['id'], $link['url']); 200 | } 201 | } 202 | 203 | if (isset($credit['text'], $credit['url'])) { 204 | $yr->setCreditText($credit['text']); 205 | $yr->setCreditUrl($credit['url']); 206 | } 207 | 208 | $yr->setLastUpdated( 209 | \DateTime::createFromFormat(self::XML_DATE_FORMAT, $meta['lastupdate']) 210 | ); 211 | $yr->setNextUpdate( 212 | \DateTime::createFromFormat(self::XML_DATE_FORMAT, $meta['nextupdate']) 213 | ); 214 | 215 | if (isset($sun['set'], $sun['rise'])) { 216 | $yr->setSunset(\DateTime::createFromFormat(self::XML_DATE_FORMAT, $sun['set'])); 217 | $yr->setSunrise(\DateTime::createFromFormat(self::XML_DATE_FORMAT, $sun['rise'])); 218 | } 219 | 220 | // Finally return the object 221 | return $yr; 222 | } catch (\Exception $e) { 223 | // We fall back and send exception if something goes wrong 224 | throw new \RuntimeException("Could not create Location object. Message: " . $e->getMessage()); 225 | } 226 | } 227 | 228 | /** 229 | * Converts xml to array and hide comments. 230 | * 231 | * @param array|\SimpleXMLElement $data xml data 232 | * 233 | * @return array 234 | */ 235 | public static function xmlToArray($data) 236 | { 237 | $out = array(); 238 | 239 | foreach ((array) $data as $index => $node) { 240 | if ($index == 'comment') { 241 | continue; 242 | } 243 | 244 | if (is_object($node) || is_array($node)) { 245 | $value = self::xmlToArray($node); 246 | } else { 247 | $value = (string) $node; 248 | } 249 | 250 | if ($index == '@attributes') { 251 | $out = array_merge($out, $value); 252 | } else { 253 | $out[$index] = $value; 254 | } 255 | } 256 | 257 | return $out; 258 | } 259 | 260 | /** 261 | * @param \SimpleXMLElement $xml 262 | * 263 | * @return WeatherStation[] 264 | */ 265 | public static function getWeatherStationsFromXml(\SimpleXMLElement $xml) 266 | { 267 | $weather_stations = array(); 268 | if (!empty($xml->observations)) { 269 | foreach ($xml->observations->weatherstation as $observation) { 270 | try { 271 | $weather_stations[] = WeatherStation::getWeatherStationFromXml($observation); 272 | } catch (\Exception $e) { 273 | // Skip those we cant create.. 274 | } 275 | } 276 | } 277 | 278 | return $weather_stations; 279 | } 280 | 281 | /** 282 | * @param \SimpleXMLElement $xml 283 | * 284 | * @return TextualForecast[] 285 | */ 286 | public static function getTextualForecastsFromXml(\SimpleXMLElement $xml) 287 | { 288 | $textual_forecasts = array(); 289 | 290 | // Some places to not have textual forecasts 291 | if (!empty($xml->forecast->text)) { 292 | foreach ($xml->forecast->text->location->time as $forecast) { 293 | try { 294 | $textual_forecasts[] = TextualForecast::createTextualForecastFromXml($forecast); 295 | } catch (\Exception $e) { 296 | // Skip those we cant create.. 297 | } 298 | } 299 | } 300 | 301 | return $textual_forecasts; 302 | } 303 | 304 | /** 305 | * @param \SimpleXMLElement $xml 306 | * @return Forecast[] 307 | */ 308 | public static function getForecastsFromXml(\SimpleXMLElement $xml) 309 | { 310 | $forecasts = array(); 311 | foreach ($xml->forecast->tabular->time as $forecast) { 312 | try { 313 | $forecasts[] = Forecast::getForecastFromXml($forecast); 314 | } catch (\RuntimeException $e) { 315 | // Skip those we cant create.. 316 | } 317 | } 318 | 319 | return $forecasts; 320 | } 321 | 322 | /** 323 | * Downloads the data from url and store in cache. 324 | * 325 | * @param String $url 326 | * @param String $path 327 | * @param integer $cacheLife 328 | * @return String XML content 329 | */ 330 | private static function downloadData($url, $path, $cacheLife) 331 | { 332 | if (!is_readable($path) || ((time() - filemtime($path)) > $cacheLife)) { 333 | $xml_content = file_get_contents($url); 334 | 335 | if (!empty($xml_content)) { 336 | // Only cache if there is content from request 337 | file_put_contents($path, $xml_content); 338 | return $xml_content; 339 | } 340 | } 341 | return file_get_contents($path); 342 | } 343 | 344 | /** 345 | * @param String $language lowercase language string 346 | * 347 | * @return String 348 | */ 349 | private static function getApiUrlByLanguage($language) 350 | { 351 | switch ($language) { 352 | case "norwegian": 353 | case "norsk": 354 | case "bokmål": 355 | return self::API_URL."sted/"; 356 | break; 357 | case "newnorwegian": 358 | case "nynorsk": 359 | return self::API_URL."stad/"; 360 | break; 361 | case "sami": 362 | case "northernsami": 363 | case "davvisámegiella": 364 | return self::API_URL."sadji/"; 365 | break; 366 | case "kven": 367 | case "kväani": 368 | return self::API_URL."paikka/"; 369 | break; 370 | default: 371 | return self::API_URL."place/"; 372 | } 373 | } 374 | 375 | /** 376 | * Checks the response from yr service. 377 | * 378 | * @see getUrlResponseCode() 379 | * 380 | * @param String $url the urls 381 | * 382 | * @return int the response 383 | */ 384 | private static function getServiceResponseCode($url) 385 | { 386 | // Check first url 387 | $url1 = self::getUrlResponseCode($url."/forecast_hour_by_hour.xml"); 388 | 389 | // If the url is ok, test the other one 390 | if ($url1 == self::SERVICE_OK) { 391 | $url2 = self::getUrlResponseCode($url."/forecast.xml"); 392 | 393 | // Since url1 is ok, return code for url2 394 | return $url2; 395 | } 396 | 397 | return $url1; 398 | } 399 | 400 | /** 401 | * There has been found a bug in the yr service that will deny access to the xml files. 402 | * This method tries to work out this issue. 403 | * 404 | * The problem is that if you try a city that has not been visited for a while, you will 405 | * get HTTP response 500 from yr. This will go away after 5-10 requests to yr. So we 406 | * will need to send at least 5 requests if we get HTTP 500. 407 | * 408 | * Thanks to https://github.com/prebenlm for finding the bug 409 | * 410 | * @param String $url full url to the endpoint 411 | * 412 | * @return int Status code 413 | */ 414 | private static function getUrlResponseCode($url) 415 | { 416 | for ($i = 0; $i < 7; $i++) { 417 | $resource = curl_init($url); 418 | curl_setopt($resource, CURLOPT_NOBODY, true); 419 | curl_exec($resource); 420 | $retcode = curl_getinfo($resource, CURLINFO_HTTP_CODE); 421 | $type = curl_getinfo($resource, CURLINFO_CONTENT_TYPE); 422 | curl_close($resource); 423 | 424 | if ($retcode === 0) { 425 | throw new \RuntimeException("Check your internet connection"); 426 | } 427 | 428 | // This might happend for 5-10 times 429 | // just skipping to next request if this does happen 430 | if ($retcode == 500) { 431 | continue; 432 | } 433 | 434 | // Response is OK, but we are not returning XML 435 | // Most likely malformatted url, like: Norway/Akershus/Nes 436 | if (($retcode == 200 && $type != "text/xml; charset=utf-8") 437 | || $retcode == 404) { 438 | return self::SERVICE_LOCATION_INVALID; 439 | } 440 | 441 | // Response is OK, and the format is xml. Lets go with that 442 | if ($retcode == 200 && $type == "text/xml; charset=utf-8") { 443 | return self::SERVICE_OK; 444 | } 445 | } 446 | 447 | return self::SERVICE_UNKNOWN_STATE; 448 | } 449 | } 450 | -------------------------------------------------------------------------------- /autoload.php: -------------------------------------------------------------------------------- 1 | =5.3.0" 16 | }, 17 | 18 | "autoload": { 19 | "psr-0": { 20 | "Yr" : "." 21 | } 22 | }, 23 | 24 | "extra": { 25 | "branch-alias": { 26 | "dev-master": "1.0-dev" 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /examples/README.md: -------------------------------------------------------------------------------- 1 | #### Current Forecast 2 | ```php 3 | $yr = Yr\Yr::create("Norway/Oslo/Oslo/Oslo", "/tmp"); 4 | 5 | $forecast = $yr->getCurrentForecast(); 6 | printf("Time: %s to %s\n", $forecast->getFrom()->format("H:i"), $forecast->getTo()->format("H:i")); 7 | printf("Temp: %s %s \n", $forecast->getTemperature(), $forecast->getTemperature('unit')); 8 | printf("Wind: %smps %s\n", $forecast->getWindSpeed(), $forecast->getWindDirection('name')); 9 | ``` 10 | ``` 11 | Time: 16:00 to 17:00 12 | Temp: 8 celsius 13 | Wind: 5.7mps South-southwest 14 | ``` 15 | 16 | #### Forecasts in range / hourly forecasts 17 | ```php 18 | $yr = Yr\Yr::create("Norway/Vestfold/Sandefjord/Sandefjord", "/tmp"); 19 | 20 | foreach($yr->getHourlyForecasts(strtotime("now"), strtotime("tomorrow")) as $forecast) { 21 | printf("Time: %s, %s degrees\n", $forecast->getFrom()->format("H:i"), $forecast->getTemperature()); 22 | } 23 | ``` 24 | ``` 25 | Time: 16:00, 7 degrees 26 | Time: 17:00, 7 degrees 27 | Time: 18:00, 6 degrees 28 | Time: 19:00, 6 degrees 29 | [...] 30 | ``` 31 | 32 | #### Meta info 33 | ```php 34 | $yr = Yr\Yr::create("Norway/Vestfold/Sandefjord/Sandefjord", "/tmp/"); 35 | 36 | echo "Location: " . $yr->getName() . "\n"; 37 | echo "Country: " . $yr->getCountry() . "\n"; 38 | echo "Last update: " . $yr->getLastUpdated()->format("d.m.y H:i") . "\n"; 39 | echo "Next update: " . $yr->getNextUpdate()->format("d.m.y H:i") . "\n"; 40 | ``` 41 | ``` 42 | Location: Sandefjord 43 | Country: Norway 44 | Last update: 09.03.14 10:38 45 | Next update: 09.03.14 17:00 46 | ``` 47 | 48 | #### Textual forecasts 49 | ```php 50 | $yr = Yr\Yr::create("Norway/Vestfold/Sandefjord/Sandefjord", "/tmp/"); 51 | 52 | foreach($yr->getTextualForecasts() as $forecast) { 53 | print $forecast->getTitle() . "\n"; 54 | print $forecast->getText() . "\n\n"; 55 | } 56 | ``` 57 | ``` 58 | Sunday and Monday 59 | Østlandet og Telemark: Sørvestlig frisk bris utsatte steder, periodevis sørvestlig liten kuling på kysten. Oppholdsvær og varierende skydekke. Lokal tåke. Svenskegrensa - Stavern: Sørvestlig frisk bris 10, periodevis liten kuling 12. Skiftende skydekke, opphold. Lokal tåke. Mandag morgen forbigående litt regn. Fra mandag formiddag vestlig bris. For det meste pent vær. 60 | 61 | Monday 62 | Østlandet: Sørvestlig bris. Skyet og lokal tåke. Forbigående litt regn sør for Oslo tidlig på dagen. I løpet av formiddagen vestlig bris og for det meste pent vær, først i vest. 63 | 64 | [...] 65 | ``` 66 | 67 | #### Weather Stations 68 | ```php 69 | $yr = Yr\Yr::create("Norway/Oslo/Oslo/Oslo", "/tmp", 10, null); 70 | 71 | $weather_stations = $yr->getWeatherStations(); 72 | 73 | foreach($weather_stations as $station) { 74 | print "Station: {$station->getName()}\n"; 75 | print "Temperature: {$station->getForecast()->getTemperature()}\n"; 76 | print "Wind Direction: {$station->getForecast()->getWindDirection()}\n"; 77 | 78 | print "\n"; 79 | } 80 | ``` 81 | ``` 82 | Station: Oslo (Blindern) 83 | Temperature: 7.6 84 | Wind Direction: SSW 85 | 86 | Station: Bygdøy 87 | Temperature: 7.9 88 | Wind Direction: 89 | 90 | Station: Alna 91 | Temperature: 8.1 92 | Wind Direction: SSW 93 | ``` -------------------------------------------------------------------------------- /examples/current_forecast.php: -------------------------------------------------------------------------------- 1 | getCurrentForecast(); 8 | printf("Time: %s to %s\n", $forecast->getFrom()->format("H:i"), $forecast->getTo()->format("H:i")); 9 | printf("Temp: %s %s \n", $forecast->getTemperature(), $forecast->getTemperature('unit')); 10 | printf("Wind: %smps %s\n", $forecast->getWindSpeed(), $forecast->getWindDirection('name')); 11 | -------------------------------------------------------------------------------- /examples/forecasts_in_range.php: -------------------------------------------------------------------------------- 1 | getHourlyForecasts(strtotime("now"), strtotime("tomorrow")) as $forecast) { 8 | echo sprintf("Time: %s, %s degrees\n", $forecast->getFrom()->format("H:i"), $forecast->getTemperature()); 9 | } 10 | -------------------------------------------------------------------------------- /examples/hourly_forecasts.php: -------------------------------------------------------------------------------- 1 | getHourlyForecasts() as $forecast) { 8 | print $forecast->getFrom()->format("H:i").": ".$forecast->getTemperature()."\n"; 9 | } 10 | -------------------------------------------------------------------------------- /examples/meta_info.php: -------------------------------------------------------------------------------- 1 | getName()."\n"; 8 | echo "Country: ".$yr->getCountry()."\n"; 9 | echo "Last update: ".$yr->getLastUpdated()->format("d.m.y H:i")."\n"; 10 | echo "Next update: ".$yr->getNextUpdate()->format("d.m.y H:i")."\n"; 11 | -------------------------------------------------------------------------------- /examples/periodic_forecasts.php: -------------------------------------------------------------------------------- 1 | getPeriodicForecasts() as $forecast) { 8 | print $forecast->getFrom()->format("H:i").": ".$forecast->getTemperature()."\n"; 9 | } 10 | -------------------------------------------------------------------------------- /examples/textual.php: -------------------------------------------------------------------------------- 1 | getTextualForecasts() as $forecast) { 8 | print $forecast->getTitle()."\n"; 9 | print $forecast->getText()."\n\n"; 10 | } 11 | -------------------------------------------------------------------------------- /examples/weather_stations.php: -------------------------------------------------------------------------------- 1 | getWeatherStations(); 8 | 9 | foreach ($weather_stations as $station) { 10 | print "Station: {$station->getName()}\n"; 11 | print "Temperature: {$station->getForecast()->getTemperature()}\n"; 12 | print "Wind Direction: {$station->getForecast()->getWindDirection()}\n"; 13 | 14 | print "\n"; 15 | } 16 | -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | tests 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /scripts/download_symbols.php: -------------------------------------------------------------------------------- 1 | /dev/null 2>&1 &"); 112 | setProgress(++$counter, "wind"); 113 | 114 | for ($i = 0; $i <= 1000; $i += 25) { 115 | $num = str_pad($i, 4, "0", STR_PAD_LEFT); 116 | 117 | for ($angle_deg = 0; $angle_deg <= 355; $angle_deg += 5) { 118 | $angle = str_pad($angle_deg, 3, "0", STR_PAD_LEFT); 119 | exec("cd $path_wind && $downloader https://fil.nrk.no/yr/grafikk/vindpiler/32/vindpil.$num.$angle.png > /dev/null 2>&1 &"); 120 | setProgress(++$counter, "wind"); 121 | } 122 | } 123 | 124 | // Download sun icons 125 | // Forecast::getSymbol("var") 126 | foreach ($images as $image) { 127 | exec("cd $path_general && $downloader https://symbol.yr.no/grafikk/sym/b200/$image.png > /dev/null 2>&1 &"); 128 | setProgress(++$counter, "sun"); 129 | } 130 | 131 | // Download moon icons 132 | // Forecast::getSymbol("var") 133 | for ($i = 0; $i <= 99; $i++) { 134 | $num = str_pad($i, 2, "0", STR_PAD_LEFT); 135 | foreach ($images_mf as $image_mf) { 136 | exec("cd $path_moon && $downloader https://symbol.yr.no/grafikk/sym/b200/mf/$image_mf.$num.png > /dev/null 2>&1 &"); 137 | setProgress(++$counter, "moon"); 138 | } 139 | } 140 | 141 | echo "\nDone! Downloaded $counter files\n"; 142 | 143 | // A function to update counter 144 | function setProgress($current, $thing) 145 | { 146 | global $max; 147 | global $sleep_time; 148 | echo "\r" . chr(27) . "[K" . "Downloading... ($thing) ($current files) " . ((int)(($current / $max) * 100)) . "%"; 149 | usleep($sleep_time); 150 | } 151 | -------------------------------------------------------------------------------- /scripts/symbols/dummy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eigan/yr-php-library/2c2059a9b6596d1b56e9fdaf4cce0e2355b6d6e7/scripts/symbols/dummy -------------------------------------------------------------------------------- /test.php: -------------------------------------------------------------------------------- 1 | include "test.php"; 8 | */ 9 | 10 | // Include the classfiles once 11 | include_once "Yr/Yr.php"; 12 | include_once "Yr/Forecast.php"; 13 | 14 | // This is not set by default in ini (OSX), 15 | // so override to standard norwegian to avoid warnings 16 | date_default_timezone_set("Europe/Oslo"); 17 | 18 | // Saving you 19 | set_error_handler(function ($errno, $msg) { 20 | // Just print the error 21 | echo "PHP Error: ".$msg."\n"; 22 | 23 | // Do not go to internal php error handler 24 | return true; 25 | }); 26 | 27 | // Possible to override default values 28 | $location = !isset($location) ? "Norway/Vestfold/Sandefjord/Sandefjord" : $location; 29 | $cache_path = !isset($cache_path) ? "/tmp" : $cache_path; 30 | $cache_life = !isset($cache_life) ? 10 : $cache_life; 31 | $language = !isset($language) ? "english" : $language; 32 | 33 | // Initialize yr object 34 | $yr = Yr\Yr::create($location, $cache_path, $cache_life, $language); 35 | 36 | // Notice user if the yr object was created 37 | if ($yr instanceof Yr\Yr) { 38 | print "An Yr object is now available on ".'$yr'; 39 | } 40 | -------------------------------------------------------------------------------- /tests/Yr/ForecastTest.php: -------------------------------------------------------------------------------- 1 | yr = Yr\Yr::create("Norway/Oslo/Oslo/Oslo", "/tmp"); 8 | $this->forecast = $this->yr->getCurrentForecast(); 9 | } 10 | 11 | public function testIsForecast() 12 | { 13 | $this->assertInstanceOf("Yr\Forecast", $this->forecast); 14 | } 15 | 16 | /** 17 | * Testing missing data from xml 18 | * 19 | * @expectedException \RuntimeException 20 | */ 21 | public function testGetForecastFromXmlInvalid() 22 | { 23 | Yr\Forecast::getForecastFromXml(new \SimpleXMLElement(' ')); 30 | } 31 | 32 | /** 33 | * Testing missing data from xml 34 | * 35 | * @expectedException \RuntimeException 36 | */ 37 | public function testGetForecastFromXmlInvalid2() 38 | { 39 | Yr\Forecast::getForecastFromXml(new \SimpleXMLElement(' ')); 46 | } 47 | 48 | public function testGetSymbol() 49 | { 50 | $symbol = $this->forecast->getSymbol(); 51 | $this->assertTrue(is_string($symbol) && !empty($symbol)); 52 | $symbol = $this->forecast->getSymbol("number"); 53 | $this->assertTrue(is_string($symbol) && !empty($symbol)); 54 | $symbol = $this->forecast->getSymbol("name"); 55 | $this->assertTrue(is_string($symbol) && !empty($symbol)); 56 | $symbol = $this->forecast->getSymbol("var"); 57 | $this->assertTrue(is_string($symbol) && !empty($symbol)); 58 | } 59 | 60 | public function testGetPrecipitation() 61 | { 62 | $precipitation = $this->forecast->getPrecipitation(); 63 | $this->assertTrue(!empty($precipitation) || $precipitation === "0"); 64 | } 65 | 66 | public function testSetPrecipitation() 67 | { 68 | $testArray = array("value" => "1", 'minvalue' => 1, 'maxvalue' => 1); 69 | $this->forecast->setPrecipitation($testArray); 70 | 71 | $this->assertEquals($this->forecast->getPrecipitation("value"), $testArray['value']); 72 | $this->assertEquals($this->forecast->getPrecipitation("minvalue"), $testArray['minvalue']); 73 | $this->assertEquals($this->forecast->getPrecipitation("maxvalue"), $testArray['maxvalue']); 74 | } 75 | 76 | public function testGetWindDirection() 77 | { 78 | $wind_direction = $this->forecast->getWindDirection(); 79 | $this->assertTrue(is_string($wind_direction) && !empty($wind_direction)); 80 | $wind_direction = $this->forecast->getWindDirection("deg"); 81 | $this->assertTrue(is_string($wind_direction) && !empty($wind_direction)); 82 | $wind_direction = $this->forecast->getWindDirection("code"); 83 | $this->assertTrue(is_string($wind_direction) && !empty($wind_direction)); 84 | $wind_direction = $this->forecast->getWindDirection("name"); 85 | $this->assertTrue(is_string($wind_direction) && !empty($wind_direction)); 86 | } 87 | 88 | public function testGetTemperature() 89 | { 90 | $temperature = $this->forecast->getTemperature(); 91 | $this->assertTrue(is_string($temperature) && ($temperature == 0 || !empty($temperature))); 92 | $temperature = $this->forecast->getTemperature("unit"); 93 | $this->assertTrue(is_string($temperature) && !empty($temperature)); 94 | $temperature = $this->forecast->getTemperature("value"); 95 | $this->assertTrue(is_string($temperature) && ($temperature == 0 || !empty($temperature))); 96 | } 97 | 98 | public function testGetPressure() 99 | { 100 | $pressure = $this->forecast->getPressure(); 101 | $this->assertTrue(is_string($pressure) && !empty($pressure)); 102 | $pressure = $this->forecast->getPressure("unit"); 103 | $this->assertTrue(is_string($pressure) && !empty($pressure)); 104 | $pressure = $this->forecast->getPressure("value"); 105 | $this->assertTrue(is_string($pressure) && !empty($pressure)); 106 | } 107 | 108 | public function testSetPressure() 109 | { 110 | $testArray = array("unit" => "m", 'value' => 1); 111 | $this->forecast->setPressure($testArray); 112 | 113 | $this->assertEquals($this->forecast->getPressure("unit"), $testArray['unit']); 114 | $this->assertEquals($this->forecast->getPressure("value"), $testArray['value']); 115 | } 116 | 117 | public function testGetWindIconKey() 118 | { 119 | $icon = $this->forecast->getWindIconKey(); 120 | $this->assertTrue(!empty($icon) && is_string($icon)); 121 | } 122 | 123 | public function testGetWindIconKey0() 124 | { 125 | $this->forecast->setWindSpeed(array('mps' => 0.2, 'name' => "slow")); 126 | 127 | $this->assertEquals(0, $this->forecast->getWindIconKey()); 128 | } 129 | 130 | public function testGetPeriod() 131 | { 132 | $period = $this->forecast->getPeriod(); 133 | $this->assertTrue(is_numeric($period) || is_null($period)); 134 | } 135 | 136 | public function testSetPeriod() 137 | { 138 | $this->forecast->setPeriod(1); 139 | $this->assertEquals(1, $this->forecast->getPeriod()); 140 | } 141 | 142 | public function testSetFrom() 143 | { 144 | $date = new \DateTime(); 145 | $this->forecast->setFrom($date); 146 | 147 | $this->assertEquals($date, $this->forecast->getFrom()); 148 | } 149 | 150 | public function testSetTo() 151 | { 152 | $date = new \DateTime(); 153 | $this->forecast->setTo($date); 154 | 155 | $this->assertEquals($date, $this->forecast->getTo()); 156 | } 157 | 158 | public function testToArray() 159 | { 160 | $array = $this->forecast->toArray(); 161 | 162 | $this->assertTrue(is_array($array)); 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /tests/Yr/LocationTest.php: -------------------------------------------------------------------------------- 1 | location = Yr\Yr::create("Norway/Oslo/Oslo/Oslo", "/tmp"); 8 | } 9 | public function testGetHourlyForecasts() 10 | { 11 | $forecasts = $this->location->getHourlyForecasts(); 12 | $this->assertTrue(is_array($forecasts) && count($forecasts) > 0); 13 | 14 | $forecasts = $this->location->getHourlyForecasts(strtotime("now"), strtotime("tomorrow")); 15 | $this->assertTrue(is_array($forecasts) && count($forecasts) > 0); 16 | } 17 | 18 | public function testGetPeriodicForecasts() 19 | { 20 | $forecasts = $this->location->getPeriodicForecasts(); 21 | $this->assertTrue(is_array($forecasts) && count($forecasts) > 0); 22 | 23 | $forecast = reset($forecasts); 24 | $this->assertInstanceOf("Yr\Forecast", $forecast); 25 | 26 | $forecasts = $this->location->getPeriodicForecasts(strtotime("now"), strtotime("tomorrow")); 27 | $this->assertTrue(is_array($forecasts) && count($forecasts) > 0); 28 | 29 | $forecast = reset($forecasts); 30 | $this->assertInstanceOf("Yr\Forecast", $forecast); 31 | } 32 | 33 | public function testGetForecastAt() 34 | { 35 | $this->assertInstanceOf("Yr\Forecast", $this->location->getForecastAt(strtotime("now"))); 36 | } 37 | 38 | public function testGetWeatherStations() 39 | { 40 | $stations = $this->location->getWeatherStations(); 41 | $this->assertTrue(is_array($stations) && count($stations) > 0); 42 | 43 | $station = reset($stations); 44 | $this->assertInstanceOf("Yr\WeatherStation", $station); 45 | } 46 | 47 | public function testGetName() 48 | { 49 | $name = $this->location->getName(); 50 | $this->assertTrue(is_string($name) && !empty($name)); 51 | } 52 | 53 | public function testGetType() 54 | { 55 | $type = $this->location->getType(); 56 | $this->assertTrue(is_string($type) && !empty($type)); 57 | } 58 | 59 | public function testGetCountry() 60 | { 61 | $country = $this->location->getCountry(); 62 | $this->assertTrue(is_string($country) && !empty($country)); 63 | } 64 | 65 | public function testGetTimezone() 66 | { 67 | $timezone = $this->location->getTimezone(); 68 | $this->assertTrue(is_string($timezone) && !empty($timezone)); 69 | } 70 | 71 | public function testgetLatLong() 72 | { 73 | $latLong = $this->location->getLatLong(); 74 | 75 | $this->assertTrue(is_array($latLong)); 76 | 77 | $this->assertArrayHasKey("lat", $latLong); 78 | $this->assertArrayHasKey("long", $latLong); 79 | } 80 | 81 | public function testGetCredit() 82 | { 83 | $credit_text = $this->location->getCreditText(); 84 | $this->assertTrue(is_string($credit_text) && !empty($credit_text)); 85 | $credit_url = $this->location->getCreditUrl(); 86 | $this->assertTrue(is_string($credit_url) && !empty($credit_url)); 87 | } 88 | 89 | public function testGetLinks() 90 | { 91 | $this->assertTrue(is_array($this->location->getLinks())); 92 | } 93 | 94 | public function testGetCurrentForecast() 95 | { 96 | $this->assertInstanceOf("Yr\Forecast", $this->location->getCurrentForecast()); 97 | } 98 | 99 | public function testGetSunrise() 100 | { 101 | $this->assertInstanceOf("\Datetime", $this->location->getSunrise()); 102 | } 103 | 104 | public function testGetSunset() 105 | { 106 | $this->assertInstanceOf("\Datetime", $this->location->getSunset()); 107 | } 108 | 109 | public function testGetNextUpdate() 110 | { 111 | $this->assertInstanceOf("\Datetime", $this->location->getNextUpdate()); 112 | } 113 | 114 | public function testGetLastUpdated() 115 | { 116 | $this->assertInstanceOf("\Datetime", $this->location->getLastUpdated()); 117 | } 118 | 119 | public function testToArray() 120 | { 121 | $array = $this->location->toArray(); 122 | 123 | $this->assertTrue(is_array($array)); 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /tests/Yr/TextualForecastTest.php: -------------------------------------------------------------------------------- 1 | yr = Yr\Yr::create("Norway/Oslo/Oslo/Oslo", "/tmp"); 8 | $forecasts = $this->yr->getTextualForecasts(); 9 | $this->forecast = reset($forecasts); 10 | } 11 | 12 | /** 13 | * @expectedException \InvalidArgumentException 14 | */ 15 | public function testInvalidForecast() 16 | { 17 | new Yr\TextualForecast("", "", new \Datetime()); 18 | } 19 | 20 | public function testGetTitle() 21 | { 22 | $title = $this->forecast->getTitle(); 23 | 24 | $this->assertTrue(is_string($title) && !empty($title)); 25 | } 26 | 27 | public function testGetText() 28 | { 29 | $text = $this->forecast->getText(); 30 | 31 | $this->assertTrue(is_string($text) && !empty($text)); 32 | } 33 | 34 | public function testGetFrom() 35 | { 36 | $from = $this->forecast->getFrom(); 37 | $this->assertInstanceOf("\Datetime", $from); 38 | } 39 | 40 | public function testGetTo() 41 | { 42 | $to = $this->forecast->getTo(); 43 | 44 | $this->assertInstanceOf("\Datetime", $to); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /tests/Yr/WeatherStationTest.php: -------------------------------------------------------------------------------- 1 | yr = Yr\Yr::create("Norway/Oslo/Oslo/Oslo", "/tmp"); 8 | $stations = $this->yr->getWeatherStations(); 9 | $this->station = reset($stations); 10 | } 11 | 12 | public function testGetName() 13 | { 14 | $name = $this->station->getName(); 15 | $this->assertTrue(is_string($name) && !empty($name), "getName() is empty or not string"); 16 | } 17 | 18 | public function testGetDistance() 19 | { 20 | $distance = $this->station->getDistance(); 21 | $this->assertTrue(is_numeric($distance) && !empty($distance), "getDistance() is empty or not numeric"); 22 | } 23 | 24 | public function testgetLatLong() 25 | { 26 | $latLong = $this->station->getLatLong(); 27 | 28 | $this->assertTrue(is_array($latLong)); 29 | 30 | $this->assertArrayHasKey("lat", $latLong); 31 | $this->assertArrayHasKey("long", $latLong); 32 | } 33 | 34 | public function testGetSource() 35 | { 36 | $source = $this->station->getSource(); 37 | 38 | $this->assertTrue(is_string($source) && !empty($source), "getSource() is not string or empty"); 39 | } 40 | 41 | public function testGetForecast() 42 | { 43 | $forecast = $this->station->getForecast(); 44 | 45 | $this->assertInstanceOf("Yr\Forecast", $forecast); 46 | } 47 | 48 | public function testSetForecast() 49 | { 50 | $forecast = new Yr\Forecast(); 51 | $this->station->setForecast($forecast); 52 | 53 | $this->assertEquals($forecast, $this->station->getForecast()); 54 | } 55 | 56 | public function testToArray() 57 | { 58 | $array = $this->station->toArray(); 59 | 60 | $this->assertTrue(is_array($array) && count($array) > 3); 61 | 62 | $this->assertArrayHasKey('name', $array); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /tests/Yr/YrTest.php: -------------------------------------------------------------------------------- 1 | assertInstanceOf("Yr\Location", $yr); 10 | } 11 | 12 | public function testCreateFresh() 13 | { 14 | $cache_dir = "/tmp/phpyr".time(); 15 | mkdir($cache_dir); 16 | 17 | $yr = Yr\Yr::create("Norway/Oslo/Oslo/Oslo", $cache_dir, 10, "english"); 18 | $this->assertInstanceOf("Yr\Location", $yr); 19 | } 20 | 21 | public function testCreateNorwegian() 22 | { 23 | $yr = Yr\Yr::create("Norway/Oslo/Oslo/Oslo", "/tmp", 10, "norwegian"); 24 | $this->assertInstanceOf("Yr\Location", $yr); 25 | } 26 | 27 | public function testCreateNewNorwegian() 28 | { 29 | $yr = Yr\Yr::create("Norway/Oslo/Oslo/Oslo", "/tmp", 10, "newnorwegian"); 30 | $this->assertInstanceOf("Yr\Location", $yr); 31 | } 32 | 33 | /** 34 | * @expectedException \InvalidArgumentException 35 | */ 36 | public function testCreateInvalidLocationArgument() 37 | { 38 | Yr\Yr::create("", "/tmp", 10, null); 39 | } 40 | 41 | /** 42 | * @expectedException \RuntimeException 43 | */ 44 | public function testCreateInvalidLocation2() 45 | { 46 | Yr\Yr::create("5855/invalid", "/tmp", 10, null); 47 | } 48 | 49 | /** 50 | * @expectedException \RuntimeException 51 | */ 52 | public function testCreateInvalidLocation() 53 | { 54 | Yr\Yr::create("Norway/Vestfold/nocity/Nocity", "/tmp", 10, null); 55 | } 56 | 57 | /** 58 | * @expectedException \RuntimeException 59 | */ 60 | public function testCreateNotWriteableCache() 61 | { 62 | Yr\Yr::create("Norway/Oslo/Oslo/Oslo", "/this/dir/does/not/exist/", 10, null); 63 | } 64 | 65 | /** 66 | * @expectedException \InvalidArgumentException 67 | */ 68 | public function testCreateInvalidCachePath() 69 | { 70 | Yr\Yr::create("Norway/Oslo/Oslo/Oslo", "", 10, null); 71 | } 72 | } 73 | --------------------------------------------------------------------------------