home *** CD-ROM | disk | FTP | other *** search
/ Cricao de Sites - 650 Layouts Prontos / WebMasters.iso / Servidores / xampp-win32-1.6.7-installer.exe / php / PEAR / Services / Weather / Weatherdotcom.php < prev   
Encoding:
PHP Script  |  2008-07-02  |  25.6 KB  |  659 lines

  1. <?php
  2. /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker: */
  3.  
  4. /**
  5.  * PEAR::Services_Weather_Weatherdotcom
  6.  *
  7.  * PHP versions 4 and 5
  8.  *
  9.  * <LICENSE>
  10.  * Copyright (c) 2005-2007, Alexander Wirtz
  11.  * All rights reserved.
  12.  *
  13.  * Redistribution and use in source and binary forms, with or without
  14.  * modification, are permitted provided that the following conditions
  15.  * are met:
  16.  * o Redistributions of source code must retain the above copyright notice,
  17.  *   this list of conditions and the following disclaimer.
  18.  * o Redistributions in binary form must reproduce the above copyright notice,
  19.  *   this list of conditions and the following disclaimer in the documentation
  20.  *   and/or other materials provided with the distribution.
  21.  * o Neither the name of the software nor the names of its contributors
  22.  *   may be used to endorse or promote products derived from this software
  23.  *   without specific prior written permission.
  24.  *
  25.  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  26.  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  27.  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  28.  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
  29.  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  30.  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  31.  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  32.  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  33.  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  34.  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  35.  * POSSIBILITY OF SUCH DAMAGE.
  36.  * </LICENSE>
  37.  *
  38.  * @category    Web Services
  39.  * @package     Services_Weather
  40.  * @author      Alexander Wirtz <alex@pc4p.net>
  41.  * @copyright   2005-2007 Alexander Wirtz
  42.  * @license     http://www.opensource.org/licenses/bsd-license.php  BSD License
  43.  * @version     CVS: $Id: Weatherdotcom.php,v 1.63 2008/05/10 20:43:36 eru Exp $
  44.  * @link        http://pear.php.net/package/Services_Weather
  45.  * @link        http://www.weather.com/services/xmloap.html
  46.  * @example     examples/weather.com-basic.php      weather.com-basic.php
  47.  * @example     examples/weather.com-extensive.php  weather.com-extensive.php
  48.  * @filesource
  49.  */
  50.  
  51. require_once "Services/Weather/Common.php";
  52.  
  53. // {{{ class Services_Weather_Weatherdotcom
  54. /**
  55.  * This class acts as an interface to the xml service of weather.com. It
  56.  * searches for given locations and retrieves current weather data as well
  57.  * as forecast for up to 5 days.
  58.  *
  59.  * For using the weather.com xml-service please visit
  60.  *     http://www.weather.com/services/xmloap.html
  61.  * and follow the link to sign up, it's free! You will receive an email
  62.  * where to download the SDK with the needed images and guidelines how to
  63.  * publish live data from weather.com. Unfortunately the guidelines are a
  64.  * bit harsh, that's why there's no actual data-representation in this
  65.  * class, just the raw data. Also weather.com demands active caching, so I'd
  66.  * strongly recommend enabling the caching implemented in this class. It
  67.  * obeys to the times as written down in the guidelines.
  68.  *
  69.  * For working examples, please take a look at
  70.  *     docs/Services_Weather/examples/weather.com-basic.php
  71.  *     docs/Services_Weather/examples/weather.com-extensive.php
  72.  *
  73.  * @category    Web Services
  74.  * @package     Services_Weather
  75.  * @author      Alexander Wirtz <alex@pc4p.net>
  76.  * @copyright   2005-2007 Alexander Wirtz
  77.  * @license     http://www.opensource.org/licenses/bsd-license.php  BSD License
  78.  * @version     Release: 1.4.3
  79.  * @link        http://pear.php.net/package/Services_Weather
  80.  * @link        http://www.weather.com/services/xmloap.html
  81.  * @example     examples/weather.com-basic.php      weather.com-basic.php
  82.  * @example     examples/weather.com-extensive.php  weather.com-extensive.php
  83.  */
  84. class Services_Weather_Weatherdotcom extends Services_Weather_Common {
  85.  
  86.     // {{{ properties
  87.     /**
  88.      * Partner-ID at weather.com
  89.      *
  90.      * @var     string                      $_partnerID
  91.      * @access  private
  92.      */
  93.     var $_partnerID = "";
  94.  
  95.     /**
  96.      * License key at weather.com
  97.      *
  98.      * @var     string                      $_licenseKey
  99.      * @access  private
  100.      */
  101.     var $_licenseKey = "";
  102.  
  103.     /**
  104.      * Switch to toggle pre-fetching of data in one single request
  105.      *
  106.      * @var     bool                        $_preFetch
  107.      * @access  private
  108.      */
  109.      var $_preFetch = false;
  110.  
  111.     /**
  112.      * Object containing the promotional links-data
  113.      *
  114.      * @var     object stdClass             $_links
  115.      * @access  private
  116.      */
  117.     var $_links;
  118.  
  119.     /**
  120.      * XML_Unserializer, used for processing the xml
  121.      *
  122.      * @var     object XML_Unserializer     $_unserializer
  123.      * @access  private
  124.      */
  125.     var $_unserializer;
  126.     // }}}
  127.  
  128.     // {{{ constructor
  129.     /**
  130.      * Constructor
  131.      *
  132.      * Requires XML_Serializer to be installed
  133.      *
  134.      * @param   array                       $options
  135.      * @param   mixed                       $error
  136.      * @throws  PEAR_Error
  137.      * @access  private
  138.      */
  139.     function Services_Weather_Weatherdotcom($options, &$error)
  140.     {
  141.         $perror = null;
  142.         $this->Services_Weather_Common($options, $perror);
  143.         if (Services_Weather::isError($perror)) {
  144.             $error = $perror;
  145.             return;
  146.         }
  147.  
  148.         // Set options accordingly
  149.         if (isset($options["partnerID"])) {
  150.             $this->setAccountData($options["partnerID"]);
  151.         }
  152.         if (isset($options["licenseKey"])) {
  153.             $this->setAccountData("", $options["licenseKey"]);
  154.         }
  155.         if (isset($options["preFetch"])) {
  156.             $this->enablePreFetch($options["preFetch"]);
  157.         }
  158.  
  159.         include_once "XML/Unserializer.php";
  160.         $unserializer = &new XML_Unserializer(array("complexType" => "object", "keyAttribute" => "type"));
  161.         if (Services_Weather::isError($unserializer)) {
  162.             $error = $unserializer;
  163.             return;
  164.         } else {
  165.             $this->_unserializer = $unserializer;
  166.         }
  167.  
  168.         // Can't acquire an object here, has to be clean on every request
  169.         include_once "HTTP/Request.php";
  170.     }
  171.     // }}}
  172.  
  173.     // {{{ setAccountData()
  174.     /**
  175.      * Sets the neccessary account-information for weather.com, you'll
  176.      * receive them after registering for the XML-stream
  177.      *
  178.      * @param   string                      $partnerID
  179.      * @param   string                      $licenseKey
  180.      * @access  public
  181.      */
  182.     function setAccountData($partnerID, $licenseKey)
  183.     {
  184.         if (strlen($partnerID) && ctype_digit($partnerID)) {
  185.             $this->_partnerID  = $partnerID;
  186.         }
  187.         if (strlen($licenseKey) && ctype_alnum($licenseKey)) {
  188.             $this->_licenseKey = $licenseKey;
  189.         }
  190.     }
  191.     // }}}
  192.  
  193.     // {{{ enablePreFetch()
  194.     /**
  195.      * Enables pre-fetching of data in one single request
  196.      *
  197.      * @param   bool                        $preFetch
  198.      * @access  public
  199.      */
  200.     function enablePreFetch($preFetch)
  201.     {
  202.         if ($preFetch == true) {
  203.             $this->_preFetch = true;
  204.         }
  205.     }
  206.     // }}}
  207.  
  208.     // {{{ _checkLocationID()
  209.     /**
  210.      * Checks the id for valid values and thus prevents silly requests to
  211.      * weather.com server
  212.      *
  213.      * @param   string                      $id
  214.      * @return  PEAR_Error|bool
  215.      * @throws  PEAR_Error::SERVICES_WEATHER_ERROR_NO_LOCATION
  216.      * @throws  PEAR_Error::SERVICES_WEATHER_ERROR_INVALID_LOCATION
  217.      * @access  private
  218.      */
  219.     function _checkLocationID($id)
  220.     {
  221.         if (is_array($id) || is_object($id) || !strlen($id)) {
  222.             return Services_Weather::raiseError(SERVICES_WEATHER_ERROR_NO_LOCATION, __FILE__, __LINE__);
  223.         } elseif (!ctype_alnum($id) || (strlen($id) > 8)) {
  224.             return Services_Weather::raiseError(SERVICES_WEATHER_ERROR_INVALID_LOCATION, __FILE__, __LINE__);
  225.         }
  226.  
  227.         return true;
  228.     }
  229.     // }}}
  230.  
  231.     // {{{ _parseWeatherData()
  232.     /**
  233.      * Fetches the data based on the requested type and caches it
  234.      *
  235.      * @param   string                      $id
  236.      * @param   string                      $reqType
  237.      * @return  PEAR_Error|bool
  238.      * @throws  PEAR_Error::SERVICES_WEATHER_ERROR_WRONG_SERVER_DATA
  239.      * @throws  PEAR_Error
  240.      * @access  private
  241.      */
  242.     function _parseWeatherData($id, $reqType)
  243.     {
  244.         if ($this->_preFetch) {
  245.             $reqType = "all";
  246.         }
  247.  
  248.         $url = "http://xoap.weather.com/weather/local/".$id."?link=xoap&prod=xoap&par=".$this->_partnerID."&key=".$this->_licenseKey;
  249.  
  250.         switch ($reqType) {
  251.             case "links":
  252.                 $url .= "&link=xoap";
  253.                 break;
  254.             case "weather":
  255.                 $url .= "&cc=*&unit=s";
  256.                 break;
  257.             case "forecast":
  258.                 $url .= "&dayf=5&unit=s";
  259.                 break;
  260.             case "all":
  261.                 $url .= "&link=xoap&cc=*&dayf=5&unit=s";
  262.                 break;
  263.         }
  264.  
  265.         // Get data from URL...
  266.         $request = &new HTTP_Request($url, $this->_httpOptions);
  267.         $status = $request->sendRequest();
  268.         if (Services_Weather::isError($status) || (int) $request->getResponseCode() <> 200) {
  269.             return Services_Weather::raiseError(SERVICES_WEATHER_ERROR_WRONG_SERVER_DATA, __FILE__, __LINE__);
  270.         }
  271.         $data = $request->getResponseBody();
  272.  
  273.         // ...and unserialize
  274.         $status = $this->_unserializer->unserialize($data);
  275.  
  276.         if (Services_Weather::isError($status)) {
  277.             return Services_Weather::raiseError(SERVICES_WEATHER_ERROR_WRONG_SERVER_DATA, __FILE__, __LINE__);
  278.         } else {
  279.             $root = $this->_unserializer->getRootName();
  280.             $data = $this->_unserializer->getUnserializedData();
  281.  
  282.             if (Services_Weather::isError($root) || $root == "HTML") {
  283.                 // Something wrong here, maybe not XML retrieved...
  284.                 return Services_Weather::raiseError(SERVICES_WEATHER_ERROR_WRONG_SERVER_DATA, __FILE__, __LINE__);
  285.             } elseif ($root == "error") {
  286.                 // We got an error back from weather.com
  287.                 $errno  = key(get_object_vars($data));
  288.                 return Services_Weather::raiseError($errno, __FILE__, __LINE__);
  289.             } else {
  290.                 // Valid data, lets get started
  291.                 // Loop through the different sub-parts of the data fro processing
  292.                 foreach (get_object_vars($data) as $key => $val) {
  293.                     switch ($key) {
  294.                         case "head":
  295.                             continue 2;
  296.                         case "prmo":
  297.                             $varname  = "links";
  298.                             break;
  299.                         case "loc":
  300.                             $varname  = "location";
  301.                             break;
  302.                         case "cc":
  303.                             $varname  = "weather";
  304.                             break;
  305.                         case "dayf":
  306.                             $varname  = "forecast";
  307.                             break;
  308.                     }
  309.                     // Save data in object
  310.                     $this->{"_".$varname} = $val;
  311.                     if ($this->_cacheEnabled) {
  312.                         // ...and cache if possible
  313.                         $expire = constant("SERVICES_WEATHER_EXPIRES_".strtoupper($varname));
  314.                         $this->_cache->extSave($id, $val, "", $expire, $varname);
  315.                     }
  316.                 }
  317.             }
  318.         }
  319.  
  320.         return true;
  321.     }
  322.     // }}}
  323.  
  324.     // {{{ searchLocation()
  325.     /**
  326.      * Searches IDs for given location, returns array of possible locations
  327.      * or single ID
  328.      *
  329.      * @param   string                      $location
  330.      * @param   bool                        $useFirst       If set, first ID of result-array is returned
  331.      * @return  PEAR_Error|array|string
  332.      * @throws  PEAR_Error::SERVICES_WEATHER_ERROR_WRONG_SERVER_DATA
  333.      * @throws  PEAR_Error::SERVICES_WEATHER_ERROR_UNKNOWN_LOCATION
  334.      * @access  public
  335.      */
  336.     function searchLocation($location, $useFirst = false)
  337.     {
  338.         $location = trim($location);
  339.         $locLow   = strtolower($location);
  340.         
  341.         // Check on cached data: MD5-hash of location has to be correct and the userdata has to be the same as the given location 
  342.         if ($this->_cacheEnabled && $locLow == $this->_cache->getUserData(md5($locLow), "search")) {
  343.             $search = $this->_cache->get(md5($locLow), "search");
  344.         } else {
  345.             // Get search data from server and unserialize
  346.             $request = &new HTTP_Request("http://xoap.weather.com/search/search?where=".urlencode($location), $this->_httpOptions);
  347.             $status = $request->sendRequest();
  348.             if (Services_Weather::isError($status) || (int) $request->getResponseCode() <> 200) {
  349.                 return Services_Weather::raiseError(SERVICES_WEATHER_ERROR_WRONG_SERVER_DATA, __FILE__, __LINE__);
  350.             }
  351.             $data = $request->getResponseBody();
  352.     
  353.             // ...and unserialize
  354.             $status = $this->_unserializer->unserialize($data, false, array("overrideOptions" => true, "complexType" => "array", "keyAttribute" => "id"));
  355.     
  356.             if (Services_Weather::isError($status)) {
  357.                 return Services_Weather::raiseError(SERVICES_WEATHER_ERROR_WRONG_SERVER_DATA, __FILE__, __LINE__);
  358.             }
  359.     
  360.             $root = $this->_unserializer->getRootName();
  361.             $search = $this->_unserializer->getUnserializedData();
  362.     
  363.             if (Services_Weather::isError($search) || $root == "HTML") {
  364.                 return Services_Weather::raiseError(SERVICES_WEATHER_ERROR_WRONG_SERVER_DATA, __FILE__, __LINE__);
  365.             } elseif (!is_array($search) || !sizeof($search)) {
  366.                 return Services_Weather::raiseError(SERVICES_WEATHER_ERROR_UNKNOWN_LOCATION, __FILE__, __LINE__);
  367.             }
  368.  
  369.             if ($this->_cacheEnabled) {
  370.                 // ...and cache if possible
  371.                 $expire = constant("SERVICES_WEATHER_EXPIRES_SEARCH");
  372.                 $this->_cache->extSave(md5($locLow), $search, $locLow, $expire, "search");
  373.             }
  374.         }
  375.  
  376.         if (!$useFirst && (sizeof($search) > 1)) {
  377.             $searchReturn = $search;
  378.         } elseif ($useFirst || (sizeof($search) == 1)) {
  379.             $searchReturn = key($search);
  380.         } else {
  381.             $searchReturn = array();
  382.         }
  383.  
  384.         return $searchReturn;
  385.     }
  386.     // }}}
  387.  
  388.     // {{{ searchLocationByCountry()
  389.     /**
  390.      * Returns only false, as weather.com offers no country listing via
  391.      * its XML services
  392.      *
  393.      * @param   string                      $country
  394.      * @return  bool
  395.      * @access  public
  396.      * @deprecated
  397.      */
  398.     function searchLocationByCountry($country = "")
  399.     {
  400.         return false;
  401.     }
  402.     // }}}
  403.  
  404.     // {{{ getLinks()
  405.     /**
  406.      * Returns the data for the promotional links belonging to the ID
  407.      *
  408.      * @param   string                      $id
  409.      * @return  PEAR_Error|array
  410.      * @throws  PEAR_Error
  411.      * @access  public
  412.      */
  413.     function getLinks($id = "")
  414.     {
  415.         $status = $this->_checkLocationID($id);
  416.  
  417.         if (Services_Weather::isError($status)) {
  418.             return $status;
  419.         }
  420.  
  421.         $linksReturn = array();
  422.  
  423.         if ($this->_cacheEnabled && ($links = $this->_cache->get($id, "links"))) {
  424.             // Get data from cache
  425.             $this->_links = $links;
  426.             $linksReturn["cache"] = "HIT";
  427.         } else {
  428.             // Same as in the function above...
  429.             $status = $this->_parseWeatherData($id, "links");
  430.  
  431.             if (Services_Weather::isError($status)) {
  432.                 return $status;
  433.             }
  434.             $linksReturn["cache"] = "MISS";
  435.         }
  436.  
  437.         $linksReturn["promo"] = array();
  438.         for ($i = 0; $i < sizeof($this->_links->link); $i++) {
  439.             $linksReturn["promo"][$i] = array();
  440.             $linksReturn["promo"][$i]["title"] = $this->_links->link[$i]->t;
  441.             // B0rked response (returned is &par=xoap, should be &prod=xoap), fix it
  442.             $linksReturn["promo"][$i]["link"]  = str_replace("par=", "prod=", $this->_links->link[$i]->l);
  443.             $linksReturn["promo"][$i]["link"] .= "&par=".$this->_partnerID;
  444.         }
  445.  
  446.         return $linksReturn;
  447.     }
  448.     // }}}
  449.  
  450.     // {{{ getLocation()
  451.     /**
  452.      * Returns the data for the location belonging to the ID
  453.      *
  454.      * @param   string                      $id
  455.      * @return  PEAR_Error|array
  456.      * @throws  PEAR_Error
  457.      * @access  public
  458.      */
  459.     function getLocation($id = "")
  460.     {
  461.         $status = $this->_checkLocationID($id);
  462.  
  463.         if (Services_Weather::isError($status)) {
  464.             return $status;
  465.         }
  466.  
  467.         $locationReturn = array();
  468.  
  469.         if ($this->_cacheEnabled && ($location = $this->_cache->get($id, "location"))) {
  470.             // Get data from cache
  471.             $this->_location = $location;
  472.             $locationReturn["cache"] = "HIT";
  473.         } else {
  474.             // Same as in the function above...
  475.             $status = $this->_parseWeatherData($id, "location");
  476.  
  477.             if (Services_Weather::isError($status)) {
  478.                 return $status;
  479.             }
  480.             $locationReturn["cache"] = "MISS";
  481.         }
  482.  
  483.         $locationReturn["name"]      = $this->_location->dnam;
  484.         $locationReturn["time"]      = date($this->_timeFormat, strtotime($this->_location->tm));
  485.         $locationReturn["latitude"]  = $this->_location->lat;
  486.         $locationReturn["longitude"] = $this->_location->lon;
  487.         $locationReturn["sunrise"]   = date($this->_timeFormat, strtotime($this->_location->sunr));
  488.         $locationReturn["sunset"]    = date($this->_timeFormat, strtotime($this->_location->suns));
  489.         $locationReturn["timezone"]  = $this->_location->zone;
  490.  
  491.         return $locationReturn;
  492.     }
  493.     // }}}
  494.  
  495.     // {{{ getWeather()
  496.     /**
  497.      * Returns the weather-data for the supplied location
  498.      *
  499.      * @param   string                      $id
  500.      * @param   string                      $unitsFormat
  501.      * @return  PEAR_Error|array
  502.      * @throws  PEAR_Error
  503.      * @access  public
  504.      */
  505.     function getWeather($id = "", $unitsFormat = "")
  506.     {
  507.         $status = $this->_checkLocationID($id);
  508.  
  509.         if (Services_Weather::isError($status)) {
  510.             return $status;
  511.         }
  512.  
  513.         // Get other data
  514.         $units    = $this->getUnitsFormat($unitsFormat);
  515.  
  516.         $weatherReturn = array();
  517.  
  518.         if ($this->_cacheEnabled && ($weather = $this->_cache->get($id, "weather"))) {
  519.             // Same procedure...
  520.             $this->_weather = $weather;
  521.             $weatherReturn["cache"] = "HIT";
  522.         } else {
  523.             // ...as last function
  524.             $status = $this->_parseWeatherData($id, "weather");
  525.  
  526.             if (Services_Weather::isError($status)) {
  527.                 return $status;
  528.             }
  529.             $weatherReturn["cache"] = "MISS";
  530.         }
  531.  
  532.         // Some explanation for the next two lines:
  533.         // weather.com isn't always supplying the timezone in the update string, but
  534.         // uses "Local Time" as reference, which is imho utterly stupid, because it's
  535.         // inconsistent. Well, what I do here is check for this string and if I can
  536.         // find it, I calculate the difference between the timezone at the location
  537.         // and this computers timezone. This amount of seconds is then subtracted from
  538.         // the time the update-string has delivered.
  539.         $update   = str_replace("Local Time", "", $this->_weather->lsup);
  540.         $adjustTZ = ($update == $this->_weather->lsup) ? 0 : $this->_location->zone * 3600 - date("Z");
  541.         $weatherReturn["update"]            = gmdate(trim($this->_dateFormat." ".$this->_timeFormat), strtotime($update) - $adjustTZ);
  542.         $weatherReturn["updateRaw"]         = $this->_weather->lsup;
  543.         $weatherReturn["station"]           = $this->_weather->obst;
  544.         $weatherReturn["temperature"]       = $this->convertTemperature($this->_weather->tmp, "f", $units["temp"]);
  545.         $weatherReturn["feltTemperature"]   = $this->convertTemperature($this->_weather->flik, "f", $units["temp"]);
  546.         $weatherReturn["condition"]         = $this->_weather->t;
  547.         $weatherReturn["conditionIcon"]     = $this->_weather->icon;
  548.         $weatherReturn["pressure"]          = $this->convertPressure($this->_weather->bar->r, "in", $units["pres"]);
  549.         $weatherReturn["pressureTrend"]     = $this->_weather->bar->d;
  550.         $weatherReturn["wind"]              = $this->convertSpeed($this->_weather->wind->s, "mph", $units["wind"]);
  551.         $weatherReturn["windGust"]          = $this->convertSpeed($this->_weather->wind->gust, "mph", $units["wind"]);
  552.         $weatherReturn["windDegrees"]       = $this->_weather->wind->d;
  553.         $weatherReturn["windDirection"]     = $this->_weather->wind->t;
  554.         $weatherReturn["humidity"]          = $this->_weather->hmid;
  555.         if (is_numeric($this->_weather->vis)) {
  556.             $weatherReturn["visibility"]    = $this->convertDistance($this->_weather->vis, "sm", $units["vis"]);
  557.         } else {
  558.             $weatherReturn["visibility"]    = $this->_weather->vis;
  559.         }
  560.         $weatherReturn["uvIndex"]           = $this->_weather->uv->i;
  561.         $weatherReturn["uvText"]            = $this->_weather->uv->t;
  562.         $weatherReturn["dewPoint"]          = $this->convertTemperature($this->_weather->dewp, "f", $units["temp"]);
  563.  
  564.         return $weatherReturn;
  565.     }
  566.     // }}}
  567.  
  568.     // {{{ getForecast()
  569.     /**
  570.      * Get the forecast for the next days
  571.      *
  572.      * @param   string                      $id
  573.      * @param   int                         $days           Values between 1 and 5
  574.      * @param   string                      $unitsFormat
  575.      * @return  PEAR_Error|array
  576.      * @throws  PEAR_Error
  577.      * @access  public
  578.      */
  579.     function getForecast($id = "", $days = 2, $unitsFormat = "")
  580.     {
  581.         $status = $this->_checkLocationID($id);
  582.  
  583.         if (Services_Weather::isError($status)) {
  584.             return $status;
  585.         }
  586.         if (!in_array($days, range(1, 5))) {
  587.             $days = 2;
  588.         }
  589.  
  590.         // Get other data
  591.         $units    = $this->getUnitsFormat($unitsFormat);
  592.  
  593.         $forecastReturn = array();
  594.  
  595.         if ($this->_cacheEnabled && ($forecast = $this->_cache->get($id, "forecast"))) {
  596.             // Encore...
  597.             $this->_forecast = $forecast;
  598.             $forecastReturn["cache"] = "HIT";
  599.         } else {
  600.             // ...
  601.             $status = $this->_parseWeatherData($id, "forecast");
  602.  
  603.             if (Services_Weather::isError($status)) {
  604.                 return $status;
  605.             }
  606.             $forecastReturn["cache"] = "MISS";
  607.         }
  608.  
  609.         // Some explanation for the next two lines: (same as above)
  610.         // weather.com isn't always supplying the timezone in the update string, but
  611.         // uses "Local Time" as reference, which is imho utterly stupid, because it's
  612.         // inconsistent. Well, what I do here is check for this string and if I can
  613.         // find it, I calculate the difference between the timezone at the location
  614.         // and this computers timezone. This amount of seconds is then subtracted from
  615.         // the time the update-string has delivered.
  616.         $update   = str_replace("Local Time", "", $this->_forecast->lsup);
  617.         $adjustTZ = ($update == $this->_forecast->lsup) ? 0 : $this->_location->zone * 3600 - date("Z");
  618.         $forecastReturn["update"]    = gmdate($this->_dateFormat." ".$this->_timeFormat, strtotime($update) - $adjustTZ);
  619.         $forecastReturn["updateRaw"] = $this->_forecast->lsup;
  620.         $forecastReturn["days"]      = array();
  621.  
  622.         for ($i = 0; $i < $days; $i++) {
  623.             $day = array(
  624.                 "temperatureHigh" => $this->convertTemperature($this->_forecast->day[$i]->hi, "f", $units["temp"]),
  625.                 "temperatureLow"  => $this->convertTemperature($this->_forecast->day[$i]->low, "f", $units["temp"]),
  626.                 "sunrise"         => date($this->_timeFormat, strtotime($this->_forecast->day[$i]->sunr)),
  627.                 "sunset"          => date($this->_timeFormat, strtotime($this->_forecast->day[$i]->suns)),
  628.                 "day" => array(
  629.                     "condition"     => $this->_forecast->day[$i]->part[0]->t,
  630.                     "conditionIcon" => $this->_forecast->day[$i]->part[0]->icon,
  631.                     "wind"          => $this->convertSpeed($this->_forecast->day[$i]->part[0]->wind->s, "mph", $units["wind"]),
  632.                     "windGust"      => $this->convertSpeed($this->_forecast->day[$i]->part[0]->wind->gust, "mph", $units["wind"]),
  633.                     "windDegrees"   => $this->_forecast->day[$i]->part[0]->wind->d,
  634.                     "windDirection" => $this->_forecast->day[$i]->part[0]->wind->t,
  635.                     "precipitation" => $this->_forecast->day[$i]->part[0]->ppcp,
  636.                     "humidity"      => $this->_forecast->day[$i]->part[0]->hmid
  637.                 ),
  638.                 "night" => array (
  639.                     "condition"     => $this->_forecast->day[$i]->part[1]->t,
  640.                     "conditionIcon" => $this->_forecast->day[$i]->part[1]->icon,
  641.                     "wind"          => $this->convertSpeed($this->_forecast->day[$i]->part[1]->wind->s, "mph", $units["wind"]),
  642.                     "windGust"      => $this->convertSpeed($this->_forecast->day[$i]->part[1]->wind->gust, "mph", $units["wind"]),
  643.                     "windDegrees"   => $this->_forecast->day[$i]->part[1]->wind->d,
  644.                     "windDirection" => $this->_forecast->day[$i]->part[1]->wind->t,
  645.                     "precipitation" => $this->_forecast->day[$i]->part[1]->ppcp,
  646.                     "humidity"      => $this->_forecast->day[$i]->part[1]->hmid
  647.                 )
  648.             );
  649.  
  650.             $forecastReturn["days"][] = $day;
  651.         }
  652.  
  653.         return $forecastReturn;
  654.     }
  655.     // }}}
  656. }
  657. // }}}
  658. ?>
  659.