home *** CD-ROM | disk | FTP | other *** search
/ Enter 2004 June / ENTER.ISO / files / xampp-win32-1.4.5-installer.exe / xampp / Weatherdotcom.php < prev    next >
Encoding:
PHP Script  |  2004-03-24  |  19.8 KB  |  497 lines

  1. <?php
  2. /* vim: set expandtab tabstop=4 shiftwidth=4: */
  3. // +----------------------------------------------------------------------+
  4. // | PHP version 4                                                        |
  5. // +----------------------------------------------------------------------+
  6. // | Copyright (c) 1997-2004 The PHP Group                                |
  7. // +----------------------------------------------------------------------+
  8. // | This source file is subject to version 2.0 of the PHP license,       |
  9. // | that is bundled with this package in the file LICENSE, and is        |
  10. // | available through the world-wide-web at                              |
  11. // | http://www.php.net/license/2_02.txt.                                 |
  12. // | If you did not receive a copy of the PHP license and are unable to   |
  13. // | obtain it through the world-wide-web, please send a note to          |
  14. // | license@php.net so we can mail you a copy immediately.               |
  15. // +----------------------------------------------------------------------+
  16. // | Authors: Alexander Wirtz <alex@pc4p.net>                             |
  17. // +----------------------------------------------------------------------+
  18. //
  19. // $Id: Weatherdotcom.php,v 1.34 2004/03/31 12:32:58 eru Exp $
  20.  
  21. require_once "Services/Weather/Common.php";
  22.  
  23. // {{{ class Services_Weather_Weatherdotcom
  24. /**
  25. * PEAR::Services_Weather_Weatherdotcom
  26. *
  27. * This class acts as an interface to the xml service of weather.com. It searches for given
  28. * locations and retrieves current weather data as well as forecast for up to 10 days.
  29. *
  30. * For using the weather.com xml-service please visit
  31. *     http://www.weather.com/services/xmloap.html
  32. * and follow the link to sign up, it's free! You will receive an email where to download
  33. * the SDK with the needed images and guidelines how to publish live data from weather.com.
  34. * Unfortunately the guidelines are a bit harsh, that's why there's no actual data-representation
  35. * in this class, just the raw data.
  36. * Also weather.com demands active caching, so I'd strongly recommend enabling the caching
  37. * implemented in this class. It obeys to the times as written down in the guidelines.
  38. *
  39. * For a working example, please take a look at
  40. *     docs/Services_Weather/examples/weather.com-basic.php
  41. *
  42. * @author       Alexander Wirtz <alex@pc4p.net>
  43. * @link         http://www.weather.com/services/xmloap.html
  44. * @example      docs/Services_Weather/examples/weather.com-basic.php
  45. * @package      Services_Weather
  46. * @license      http://www.php.net/license/2_02.txt
  47. * @version      1.2
  48. */
  49. class Services_Weather_Weatherdotcom extends Services_Weather_Common {
  50.  
  51.     // {{{ properties
  52.     /**
  53.     * Partner-ID at weather.com
  54.     *
  55.     * @var      string                      $_partnerID
  56.     * @access   private
  57.     */
  58.     var $_partnerID = "";
  59.  
  60.     /**
  61.     * License key at weather.com
  62.     *
  63.     * @var      string                      $_licenseKey
  64.     * @access   private
  65.     */
  66.     var $_licenseKey = "";
  67.  
  68.     /**
  69.     * XML_Unserializer, used for processing the xml
  70.     *
  71.     * @var      object XML_Unserializer     $_unserializer
  72.     * @access   private
  73.     */
  74.     var $_unserializer;
  75.     // }}}
  76.  
  77.     // {{{ constructor
  78.     /**
  79.     * Constructor
  80.     *
  81.     * Requires XML_Serializer to be installed
  82.     *
  83.     * @param    array                       $options
  84.     * @param    mixed                       $error
  85.     * @throws   PEAR_Error
  86.     * @see      Science_Weather::Science_Weather
  87.     * @access   private
  88.     */
  89.     function Services_Weather_Weatherdotcom($options, &$error)
  90.     {
  91.         $perror = null;
  92.         $this->Services_Weather_Common($options, $perror);
  93.         if (Services_Weather::isError($perror)) {
  94.             $error = $perror;
  95.             return;
  96.         }
  97.  
  98.         // Set options accordingly
  99.         if (isset($options["partnerID"])) {
  100.             $this->setAccountData($options["partnerID"]);
  101.         }
  102.         if (isset($options["licenseKey"])) {
  103.             $this->setAccountData("", $options["licenseKey"]);
  104.         }
  105.         
  106.         include_once "XML/Unserializer.php";
  107.         $unserializer = &new XML_Unserializer(array("complexType" => "object", "keyAttribute" => "type"));
  108.         if (Services_Weather::isError($unserializer)) {
  109.             $error = $unserializer;
  110.             return;
  111.         } else {
  112.             $this->_unserializer = $unserializer;
  113.         }
  114.         
  115.         // Can't acquire an object here, has to be clean on every request
  116.         include_once "HTTP/Request.php";
  117.     }
  118.     // }}}
  119.  
  120.     // {{{ setAccountData()
  121.     /**
  122.     * Sets the neccessary account-information for weather.com, you'll receive them after registering for the XML-stream
  123.     *
  124.     * @param    string                      $partnerID
  125.     * @param    string                      $licenseKey
  126.     * @access   public
  127.     */
  128.     function setAccountData($partnerID, $licenseKey)
  129.     {
  130.         if (strlen($partnerID) && ctype_digit($partnerID)) {
  131.             $this->_partnerID  = $partnerID;
  132.         }
  133.         if (strlen($licenseKey) && ctype_alnum($licenseKey)) {
  134.             $this->_licenseKey = $licenseKey;
  135.         }
  136.     }
  137.     // }}}
  138.  
  139.     // {{{ _checkLocationID()
  140.     /**
  141.     * Checks the id for valid values and thus prevents silly requests to weather.com server
  142.     *
  143.     * @param    string                      $id
  144.     * @return   PEAR_Error|bool
  145.     * @throws   PEAR_Error::SERVICES_WEATHER_ERROR_NO_LOCATION
  146.     * @throws   PEAR_Error::SERVICES_WEATHER_ERROR_INVALID_LOCATION
  147.     * @access   private
  148.     */
  149.     function _checkLocationID($id)
  150.     {
  151.         if (!strlen($id)) {
  152.             return Services_Weather::raiseError(SERVICES_WEATHER_ERROR_NO_LOCATION);
  153.         } elseif (!ctype_alnum($id) || (strlen($id) > 8)) {
  154.             return Services_Weather::raiseError(SERVICES_WEATHER_ERROR_INVALID_LOCATION);
  155.         }
  156.  
  157.         return true;
  158.     }
  159.     // }}}
  160.  
  161.     // {{{ _parseWeatherData()
  162.     /**
  163.     * Parses the data returned by the provided URL and caches it
  164.     *
  165.     * @param    string                      $id
  166.     * @param    string                      $url
  167.     * @return   PEAR_Error|bool
  168.     * @throws   PEAR_Error::SERVICES_WEATHER_ERROR_WRONG_SERVER_DATA
  169.     * @throws   PEAR_Error
  170.     * @access   private
  171.     */
  172.     function _parseWeatherData($id, $url)
  173.     {
  174.         // Get data from URL...
  175.         $request = &new HTTP_Request($url, array("timeout" => $this->_httpTimeout));
  176.         $status = $request->sendRequest();
  177.         if (Services_Weather::isError($status)) {
  178.             return Services_Weather::raiseError(SERVICES_WEATHER_ERROR_WRONG_SERVER_DATA);
  179.         }
  180.         $data = $request->getResponseBody();
  181.         
  182.         // ...and unserialize
  183.         $status = $this->_unserializer->unserialize($data);
  184.  
  185.         if (Services_Weather::isError($status)) {
  186.             return Services_Weather::raiseError(SERVICES_WEATHER_ERROR_WRONG_SERVER_DATA);
  187.         } else {
  188.             $root = $this->_unserializer->getRootName();
  189.             $data = $this->_unserializer->getUnserializedData();
  190.  
  191.             if (Services_Weather::isError($root)) {
  192.                 // Something wrong here...
  193.                 return Services_Weather::raiseError(SERVICES_WEATHER_ERROR_WRONG_SERVER_DATA);
  194.             } elseif ($root == "error") {
  195.                 // We got an error back from weather.com
  196.                 $errno  = key(get_object_vars($data));
  197.                 return Services_Weather::raiseError($errno);
  198.             } else {
  199.                 // Valid data, lets get started
  200.                 // Loop through the different sub-parts of the data fro processing
  201.                 foreach (get_object_vars($data) as $key => $val) {
  202.                     switch ($key) {
  203.                         case "head":
  204.                             continue 2;
  205.                             break;
  206.                         case "loc":
  207.                             $varname  = "location";
  208.                             break;
  209.                         case "cc":
  210.                             $varname  = "weather";
  211.                             break;
  212.                         case "dayf":
  213.                             $varname  = "forecast";
  214.                             break;
  215.                     }
  216.                     // Save data in object
  217.                     $this->{"_".$varname} = $val;
  218.                     if ($this->_cacheEnabled) {
  219.                         // ...and cache if possible
  220.                         $expire = constant("SERVICES_WEATHER_EXPIRES_".strtoupper($varname));
  221.                         $this->_cache->extSave($id, $val, "", $expire, $varname);
  222.                     }
  223.                 }
  224.             }
  225.         }
  226.  
  227.         return true;
  228.     }
  229.     // }}}
  230.  
  231.     // {{{ searchLocation()
  232.     /**
  233.     * Searches IDs for given location, returns array of possible locations or single ID
  234.     *
  235.     * @param    string                      $location
  236.     * @param    bool                        $useFirst       If set, first ID of result-array is returned
  237.     * @return   PEAR_Error|array|string
  238.     * @throws   PEAR_Error::SERVICES_WEATHER_ERROR_WRONG_SERVER_DATA
  239.     * @throws   PEAR_Error::SERVICES_WEATHER_ERROR_UNKNOWN_LOCATION
  240.     * @access   public
  241.     */
  242.     function searchLocation($location, $useFirst = false)
  243.     {
  244.         // Get search data from server and unserialize
  245.         $searchURL = "http://xoap.weather.com/search/search?where=".urlencode(trim($location));
  246.         $status = $this->_unserializer->unserialize($searchURL, true, array("overrideOptions" => true, "complexType" => "array", "keyAttribute" => "id"));
  247.  
  248.         if (Services_Weather::isError($status)) {
  249.             return Services_Weather::raiseError(SERVICES_WEATHER_ERROR_WRONG_SERVER_DATA);
  250.         } else {
  251.             $search = $this->_unserializer->getUnserializedData();
  252.  
  253.             if (Services_Weather::isError($search)) {
  254.                 return Services_Weather::raiseError(SERVICES_WEATHER_ERROR_WRONG_SERVER_DATA);
  255.             } elseif (!is_array($search) || !sizeof($search)) {
  256.                 return Services_Weather::raiseError(SERVICES_WEATHER_ERROR_UNKNOWN_LOCATION);
  257.             } else {
  258.                 if (!$useFirst && (sizeof($search) > 1)) {
  259.                     $searchReturn = $search;
  260.                 } elseif ($useFirst || (sizeof($search) == 1)) {
  261.                     $searchReturn = key($search);
  262.                 }
  263.             }
  264.         }
  265.  
  266.         return $searchReturn;
  267.     }
  268.     // }}}
  269.  
  270.     // {{{ searchLocationByCountry()
  271.     /**
  272.     * Returns only false, as weather.com offers no country listing via 
  273.     * its XML services 
  274.     *
  275.     * @param    string                      $country
  276.     * @return   bool
  277.     * @access   public
  278.     * @deprecated
  279.     */
  280.     function searchLocationByCountry($country = "")
  281.     {
  282.         return false;
  283.     }
  284.     // }}}
  285.  
  286.     // {{{ getUnits()
  287.     /**
  288.     * Returns the units for the current query
  289.     *
  290.     * @param    string                      $id
  291.     * @param    string                      $unitsFormat
  292.     * @return   array
  293.     * @deprecated
  294.     * @access   public
  295.     */
  296.     function getUnits($id = null, $unitsFormat = "")
  297.     {
  298.         return $this->getUnitsFormat($unitsFormat);
  299.     }
  300.     // }}}
  301.  
  302.     // {{{ getLocation()
  303.     /**
  304.     * Returns the data for the location belonging to the ID
  305.     *
  306.     * @param    string                      $id
  307.     * @return   PEAR_Error|array
  308.     * @throws   PEAR_Error
  309.     * @access   public
  310.     */
  311.     function getLocation($id = "")
  312.     {
  313.         $status = $this->_checkLocationID($id);
  314.  
  315.         if (Services_Weather::isError($status)) {
  316.             return $status;
  317.         }
  318.  
  319.         $locationReturn = array();
  320.         $locationURL    = "http://xoap.weather.com/weather/local/".$id."?prod=xoap&par=".$this->_partnerID."&key=".$this->_licenseKey."&unit=s";
  321.  
  322.         if ($this->_cacheEnabled && ($location = $this->_cache->get($id, "location"))) {
  323.             // Get data from cache
  324.             $this->_location = $location;
  325.             $locationReturn["cache"] = "HIT";
  326.         } else {
  327.             // Same as in the function above...
  328.             $status = $this->_parseWeatherData($id, $locationURL);
  329.  
  330.             if (Services_Weather::isError($status)) {
  331.                 return $status;
  332.             }
  333.             $locationReturn["cache"] = "MISS";
  334.         }
  335.         $locationReturn["name"]      = $this->_location->dnam;
  336.         $locationReturn["time"]      = date($this->_timeFormat, strtotime($this->_location->tm));
  337.         $locationReturn["latitude"]  = $this->_location->lat;
  338.         $locationReturn["longitude"] = $this->_location->lon;
  339.         $locationReturn["sunrise"]   = date($this->_timeFormat, strtotime($this->_location->sunr));
  340.         $locationReturn["sunset"]    = date($this->_timeFormat, strtotime($this->_location->suns));
  341.         $locationReturn["timezone"]  = $this->_location->zone;
  342.  
  343.         return $locationReturn;
  344.     }
  345.     // }}}
  346.  
  347.     // {{{ getWeather()
  348.     /**
  349.     * Returns the weather-data for the supplied location
  350.     *
  351.     * @param    string                      $id
  352.     * @param    string                      $unitsFormat
  353.     * @return   PEAR_Error|array
  354.     * @throws   PEAR_Error
  355.     * @access   public
  356.     */
  357.     function getWeather($id = "", $unitsFormat = "")
  358.     {
  359.         $status = $this->_checkLocationID($id);
  360.  
  361.         if (Services_Weather::isError($status)) {
  362.             return $status;
  363.         }
  364.  
  365.         // Get other data
  366.         $units    = $this->getUnitsFormat($unitsFormat);
  367.  
  368.         $weatherReturn = array();
  369.         $weatherURL    = "http://xoap.weather.com/weather/local/".$id."?cc=*&prod=xoap&par=".$this->_partnerID."&key=".$this->_licenseKey."&unit=s";
  370.  
  371.         if ($this->_cacheEnabled && ($weather = $this->_cache->get($id, "weather"))) {
  372.             // Same procedure...
  373.             $this->_weather = $weather;
  374.             $weatherReturn["cache"] = "HIT";
  375.         } else {
  376.             // ...as last function
  377.             $status = $this->_parseWeatherData($id, $weatherURL);
  378.  
  379.             if (Services_Weather::isError($status)) {
  380.                 return $status;
  381.             }
  382.             $weatherReturn["cache"] = "MISS";
  383.         }
  384.         
  385.         $update  = str_replace("Local Time", "", $this->_weather->lsup);
  386.         $weatherReturn["update"]            = gmdate(trim($this->_dateFormat." ".$this->_timeFormat), strtotime($update));
  387.         $weatherReturn["updateRaw"]         = $this->_weather->lsup;
  388.         $weatherReturn["station"]           = $this->_weather->obst;
  389.         $weatherReturn["temperature"]       = $this->convertTemperature($this->_weather->tmp, "f", $units["temp"]);
  390.         $weatherReturn["feltTemperature"]   = $this->convertTemperature($this->_weather->flik, "f", $units["temp"]);
  391.         $weatherReturn["condition"]         = $this->_weather->t;
  392.         $weatherReturn["conditionIcon"]     = $this->_weather->icon;
  393.         $weatherReturn["pressure"]          = $this->convertPressure($this->_weather->bar->r, "in", $units["pres"]);
  394.         $weatherReturn["pressureTrend"]     = $this->_weather->bar->d;
  395.         $weatherReturn["wind"]              = $this->convertSpeed($this->_weather->wind->s, "mph", $units["wind"]);
  396.         $weatherReturn["windGust"]            = $this->convertSpeed($this->_weather->wind->gust, "mph", $units["wind"]);        
  397.         $weatherReturn["windDegrees"]       = $this->_weather->wind->d;
  398.         $weatherReturn["windDirection"]     = $this->_weather->wind->t;
  399.         $weatherReturn["humidity"]          = $this->_weather->hmid;
  400.         if (is_numeric($this->_weather->vis)) {
  401.             $weatherReturn["visibility"]    = $this->convertDistance($this->_weather->vis, "sm", $units["vis"]);
  402.         } else {
  403.             $weatherReturn["visibility"]    = $this->_weather->vis;
  404.         }
  405.         $weatherReturn["uvIndex"]           = $this->_weather->uv->i;
  406.         $weatherReturn["uvText"]            = $this->_weather->uv->t;
  407.         $weatherReturn["dewPoint"]          = $this->convertTemperature($this->_weather->dewp, "f", $units["temp"]);
  408.  
  409.         return $weatherReturn;
  410.     }
  411.     // }}}
  412.  
  413.     // {{{ getForecast()
  414.     /**
  415.     * Get the forecast for the next days
  416.     *
  417.     * @param    string                      $id
  418.     * @param    int                         $days           Values between 1 and 10
  419.     * @param    string                      $unitsFormat
  420.     * @return   PEAR_Error|array
  421.     * @throws   PEAR_Error
  422.     * @access   public
  423.     */
  424.     function getForecast($id = "", $days = 2, $unitsFormat = "")
  425.     {
  426.         $status = $this->_checkLocationID($id);
  427.  
  428.         if (Services_Weather::isError($status)) {
  429.             return $status;
  430.         }
  431.         if (!in_array($days, range(1, 10))) {
  432.             $days = 2;
  433.         }
  434.         
  435.         // Get other data
  436.         $units    = $this->getUnitsFormat($unitsFormat);
  437.  
  438.         $forecastReturn = array();
  439.         $forecastURL = "http://xoap.weather.com/weather/local/".$id."?dayf=10&prod=xoap&par=".$this->_partnerID."&key=".$this->_licenseKey."&unit=s";
  440.  
  441.         if ($this->_cacheEnabled && ($forecast = $this->_cache->get($id, "forecast"))) {
  442.             // Encore...
  443.             $this->_forecast = $forecast;
  444.             $forecastReturn["cache"] = "HIT";
  445.         } else {
  446.             // ...
  447.             $status = $this->_parseWeatherData($id, $forecastURL, $days);
  448.  
  449.             if (Services_Weather::isError($status)) {
  450.                 return $status;
  451.             }
  452.             $forecastReturn["cache"] = "MISS";
  453.         }
  454.  
  455.         $update = implode(" ", array_slice(explode(" ", $this->_forecast->lsup ), 0, 3));
  456.  
  457.         $forecastReturn["update"] = date($this->_dateFormat." ".$this->_timeFormat, strtotime($update));
  458.         $forecastReturn["days"]   = array();
  459.  
  460.         for ($i = 0; $i < $days; $i++) {
  461.             $day = array(
  462.                 "temperatureHigh" => $this->convertTemperature($this->_forecast->day[$i]->hi, "f", $units["temp"]),
  463.                 "temperatureLow"  => $this->convertTemperature($this->_forecast->day[$i]->low, "f", $units["temp"]),
  464.                 "sunrise"         => date($this->_timeFormat, strtotime($this->_forecast->day[$i]->sunr)),
  465.                 "sunset"          => date($this->_timeFormat, strtotime($this->_forecast->day[$i]->suns)),
  466.                 "day" => array(
  467.                     "condition"     => $this->_forecast->day[$i]->part[0]->t,
  468.                     "conditionIcon" => $this->_forecast->day[$i]->part[0]->icon,
  469.                     "wind"          => $this->convertSpeed($this->_forecast->day[$i]->part[0]->wind->s, "mph", $units["wind"]),
  470.                     "windGust"      => $this->convertSpeed($this->_forecast->day[$i]->part[0]->wind->gust, "mph", $units["wind"]),
  471.                     "windDegrees"   => $this->_forecast->day[$i]->part[0]->wind->d,
  472.                     "windDirection" => $this->_forecast->day[$i]->part[0]->wind->t,
  473.                     "precipitation" => $this->_forecast->day[$i]->part[0]->ppcp,
  474.                     "humidity"      => $this->_forecast->day[$i]->part[0]->hmid
  475.                 ),
  476.                 "night" => array (
  477.                     "condition"     => $this->_forecast->day[$i]->part[1]->t,
  478.                     "conditionIcon" => $this->_forecast->day[$i]->part[1]->icon,
  479.                     "wind"          => $this->convertSpeed($this->_forecast->day[$i]->part[1]->wind->s, "mph", $units["wind"]),
  480.                     "windGust"      => $this->convertSpeed($this->_forecast->day[$i]->part[1]->wind->gust, "mph", $units["wind"]),
  481.                     "windDegrees"   => $this->_forecast->day[$i]->part[1]->wind->d,
  482.                     "windDirection" => $this->_forecast->day[$i]->part[1]->wind->t,
  483.                     "precipitation" => $this->_forecast->day[$i]->part[1]->ppcp,
  484.                     "humidity"      => $this->_forecast->day[$i]->part[1]->hmid
  485.                 )
  486.             );
  487.  
  488.             $forecastReturn["days"][] = $day;
  489.         }
  490.  
  491.         return $forecastReturn;
  492.     }
  493.     // }}}
  494. }
  495. // }}}
  496. ?>
  497.