<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>PHP Starter &#187; Advanced</title>
	<atom:link href="http://phpstarter.net/tag/advanced/feed/" rel="self" type="application/rss+xml" />
	<link>http://phpstarter.net</link>
	<description>PHP Tips &#38; Tools From Starters to Experts</description>
	<lastBuildDate>Fri, 25 Jun 2010 14:14:24 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3</generator>
		<item>
		<title>Parse ZFP (Zone Forecast Product) Data in PHP &#8211; Option 1</title>
		<link>http://phpstarter.net/2010/03/parse-zfp-zone-forecast-product-data-in-php-option-1/</link>
		<comments>http://phpstarter.net/2010/03/parse-zfp-zone-forecast-product-data-in-php-option-1/#comments</comments>
		<pubDate>Wed, 03 Mar 2010 14:00:59 +0000</pubDate>
		<dc:creator>Andrew</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[Advanced]]></category>
		<category><![CDATA[Weather]]></category>

		<guid isPermaLink="false">http://phpstarter.net/?p=449</guid>
		<description><![CDATA[In a previous article, I covered 5 Sources of Free Weather Data for your Site, but did not provide any actual code to use the data. Since then, I covered sources #1 and #2. None of these sources had any English-formed forecasts. For that, we have to go to the Zone Forecast Product, but we [...]
No related posts.]]></description>
			<content:encoded><![CDATA[<p>In a previous article, I covered <a href="http://phpstarter.net/2008/12/5-sources-of-free-weather-data-for-your-site/">5 Sources of Free Weather Data for your Site</a>, but did not provide any actual code to use the data.  Since then, I covered sources <a href="http://phpstarter.net/2009/02/parse-weather-forecast-data-from-the-ndfd-in-php/">#1</a> and <a href="http://phpstarter.net/2009/02/parse-current-weather-conditions-data-from-the-nws-in-php/">#2</a>.  None of these sources had any English-formed forecasts.  For that, we have to go to the Zone Forecast Product, but we don&#8217;t have the luxury of XML here.  There is a healthy bit of RegEx and string manipulation, but you can do it!</p>
<p><span id="more-449"></span></p>
<h3>Where to Get the Data</h3>
<p>The data is available by browsing the <a href="http://www.weather.gov/data/">http://www.weather.gov/data/</a> directory.  Here, you will find a whole set of products available.  To access the ZFP data, you need to browse the http://www.weather.gov/data/XXX/ZFPXXX directory where &#8216;XXX&#8217; is the code for the local station from where the data is to be retrieved from.  For example, if I wanted the ZFP data for KLOT (Lockport, IL), I would call up <a href="http://www.weather.gov/data/LOT/ZFPLOT">http://www.weather.gov/data/LOT/ZFPLOT</a>.</p>
<p class="m_warning">There is an exception to this rule, however.  If you look at <a href="http://www.weather.gov/data/AFC/">http://www.weather.gov/data/AFC/</a>, they show the ZFP for two areas.  I think some of these are like this because one office is forecasting for to areas or something (correct me in the comments if I&#8217;m wrong).  So that formula above is more of a guideline for finding the right link for your area, but I found it to be 95% accurate.  (That&#8217;s still an &#8216;A&#8217;, right?)</p>
<h3>Understanding the Big Picture</h3>
<p><a href="http://phpstarter.net/wp-content/uploads/2009/07/zfp_01.png"><img src="http://phpstarter.net/wp-content/uploads/2009/07/zfp_01-150x150.png" alt="zfp_01" title="zfp_01" width="150" height="150" class="alignnone size-thumbnail wp-image-470" style="float: left; margin-right: 10px; " /></a></p>
<p>Click on the thumbnail for the explained section of this data.  Starting at the top is the header.  We don&#8217;t use this.  It contains the station ID and the type of product this is &#8211; we know that already.  It also contains the release time.  Although that is significant, that same information is in the header of the individual forecasts as well, so I will be discarding that in this example.</p>
<p>The second yellow box is the header for that specific forecast zone.  We will be using some data from this.  The zone code is the most critical because we need to know for which zone this forecast is for.  The only other piece of information in this box that I use is the Release Time, which is when the forecast was released (ironic).  This block of information is repeated at the top of every forecast in the file.</p>
<p>Below the second yellow box is the actual forecast data, which is the meat of what we are looking for.  Below that is the separator, which is means that the code block for the next forecast zone is next, and so on&#8230;</p>
<h3>Breaking up the Forecasts</h3>
<p>We only want to deal with one forecast zone at a time, so lets break them up.  First, I need to get rid of that pesky header at the top of the file.  We do that by splitting the data by the double line breaks, and removing that first paragraph from the resulting array.  Then, join it back together.</p>
<pre class="brush: php">
/* remove the file header */

/* $data = {text from http://www.weather.gov/data/LOT/ZFPLOT} */

$data = trim($data);
$data = explode(&quot;\n\n&quot;, $data);
unset($data[0], $data[1]);
$data = implode(&quot;\n\n&quot;, $data);
</pre>
<p>Now, we can split of the zone forecasts.  We simply split the text by the separator (&#8216;$$&#8217;), and keep the forecasts in an array.</p>
<pre class="brush: php">
/* separate the forecasts */

/* $data = {result from previous example} */

$data = explode(&#039;$$&#039;, $data);
/* trim off any extra line breaks */
$data = array_map(&#039;trim&#039;, $data);
</pre>
<p>Perfect.  Now we have an array or forecasts with their respective header block.</p>
<h3>Parsing the Zone Codes</h3>
<p>This is the hardest part of understanding the data.  We need to know for which geographical area the forecast is for.  Most zone forecasts are only for one zone, but it is possible that it may be for more than one.  In the above example, the zone code is ILZ014, which is zone 14 of Illinois.  Unfortunately, the zone code &#038; purge date line can be as complicated as <em>ILZ001>003-005-IAC045-163-051200-</em>, which is Illinois zone 1, 2, 3, 5 as well as Iowa counties 45 and 163.  For the full specs on how to understand this, see the <a href="http://www.weather.gov/emwin/winugc.htm">Universal Geographic Code</a> specifications.</p>
<p>Before we can do anything, we need to find the zone codes.  To do that, we need to use one heck of a regular expression, found on line 8 of the sample below.  This is what I used to verify that I am finding everything correctly.</p>
<p style="text-align: right; "><a href="/samples/449/zone_codes.php" target="_blank">Run This Example</a></p><pre name="code" class="brush: php">&lt;?php

/* get the forecast from a local cache */
/* http://www.weather.gov/data/LOT/ZFPLOT */
$data = file_get_contents('ZFPIND.txt');

/* the regular expression to find all the location codes */
$regex = '/(([A-Z]{2})(C|Z){1}([0-9]{3})((&gt;|-)[0-9]{3})*)-/';

/* for this example, we are doing a replacement to show we found everything...
	normally, something like preg_match() would be used */
$data = preg_replace($regex, '&lt;span style=&quot;background: #ff0; &quot;&gt;' . &quot;\${1}&quot; . '&lt;/span&gt;-', $data);

/* do I really need to explain this? */
echo '&lt;pre&gt;' . $data . '&lt;/pre&gt;';

/**
 * Why do I comment out the PHP closing tag?
 * See: http://phpstarter.net/2009/01/omit-the-php-closing-tag/
 */
/* ?&gt; */
</pre>
<p>We&#8217;re not done with this yet.  As you can see from the <a href="http://www.weather.gov/emwin/winugc.htm">Universal Geographic Code</a> reference page, they like to make shortcuts.  For example, they will use <em>INZ021-028>031</em> instead of <em>INZ021-INZ028-INZ029-INZ030-INZ031</em>.  From a programming standpoint, I think we both know which is better to understand.  When I put this data in a database, I need to be able to query a specific location and see if there is data for it.  So, if I store it as <em>INZ021-028>031</em>, how do I query for zone 29?  These ranges will have to be expanded.</p>
<p>I&#8217;m not going to go into great detail on how to do this&#8230;I will just provide a couple functions to do it.  Call parse_zones() like I do at line 69, and it will take care of the rest.</p>
<p style="text-align: right; "><a href="/samples/449/parse_zones.php" target="_blank">Run This Example</a></p><pre name="code" class="brush: php:collapse">&lt;?php

/**
 * The NWS combines does not repeat the state code for multiple zones...not good for our purpose
 * All we want to do here is convert ranges like INZ021-028 to INZ021-INZ028
 * We will also call the function to expand the ranges here.
 * See: http://www.weather.gov/emwin/winugc.htm
 */
function parse_zones($data)
{
	/* first, get rid of newlines */
	$data = str_replace(&quot;\n&quot;, '', $data);
	
	/* split up individual states - multiple states may be in the same forecast */
	$regex = '/(([A-Z]{2})(C|Z){1}([0-9]{3})((&gt;|-)[0-9]{3})*)-/';
	
	$count = preg_match_all($regex, $data, $matches);
	$total_zones = '';
	
	foreach ($matches[0] as $field =&gt; $value)
	{
		/* since the NWS thought it was efficient to not repeat state codes, we have to reverse that */
		$state = substr($value, 0, 3);
		$zones = substr($value, 3);
		
		/* convert ranges like 014&gt;016 to 014-015-016 */
		$zones = expand_ranges($zones);
		
		/* hack off the last dash */
		$zones = substr($zones, 0, strlen($zones) - 1);
		$zones = $state . str_replace('-', '-'.$state, $zones);
		
		$total_zones .= $zones;
	}
	
	
	$total_zones = explode('-', $total_zones);
	return $total_zones;
}

/**
 * The NWS combines multiple zones into ranges...not good for our purpose
 * All we want to do here is convert ranges like 014&gt;016 to 014-015-016
 * See: http://www.weather.gov/emwin/winugc.htm
 */
function expand_ranges($data)
{
	$regex = '/(([0-9]{3})(&gt;[0-9]{3}))/';
	
	$count = preg_match_all($regex, $data, $matches);
	
	foreach ($matches[0] as $field =&gt; $value)
	{
		list($start, $end) = explode('&gt;', $value);
		
		$new_value = array();
		for ($i = $start; $i &lt;= $end; $i++)
		{
			$new_value[] = str_pad($i, 3, '0', STR_PAD_LEFT);
		}
		
		$data = str_replace($value, implode('-', $new_value), $data);
	}
	
	return $data;
}


$zones = parse_zones(&quot;INZ021-028&gt;031-035-036-190915-&quot;);

header('Content-type: text/plain');
var_dump($zones);

/**
 * Why do I comment out the PHP closing tag?
 * See: http://phpstarter.net/2009/01/omit-the-php-closing-tag/
 */
/* ?&gt; */
</pre>
<h3>Putting it All Together</h3>
<p>So we know how to download the latest forecast data for a specific station, split it up, and get the zones for each one.  Now it&#8217;s time to put it together and in a format where we can store or display it.  In this example, I am adding some more basic PHP to form a large array with the forecast block and zones in each element.  From there, you can easily place the data in a database or whatever.</p>
<p class="m_info"><strong>Note:</strong> Note that I am using data from KIND and not KLOT in this example, because KIND has forecast blocks that cover multiple zones, showing how parse_zones() work.</p>
<p style="text-align: right; "><a href="/samples/449/zfp.php" target="_blank">Run This Example</a></p><pre name="code" class="brush: php">&lt;?php

/**
 * adds the two functions from the previous example:
 * parse_zines()
 * parse_ranges()
 */
include('functions.php');

$data = file_get_contents('ZFPIND.txt');

/* remove the file header */
$data = trim($data);
$data = explode(&quot;\n\n&quot;, $data);
unset($data[0], $data[1]);
$data = implode(&quot;\n\n&quot;, $data);


/* separate the forecasts */
$data = explode('$$', $data);
/* trim off any extra line breaks */
$data = array_map('trim', $data);

$data_form = array();
foreach ($data as $field =&gt; $value)
{
	$lines = explode(&quot;\n&quot;, $value);
	$zones = parse_zones($lines[0]);
	
	$blocks = explode(&quot;\n\n&quot;, $value);
	$forecast = $blocks[1];
	
	$data_time = parse_date_time($value);
	
	$data_form[] = array('zones' =&gt; $zones, 'date_time' =&gt; $date_time, 'forecast' =&gt; $forecast);
}


header('Content-type: text/plain');
var_dump($data_form);


/**
 * Why do I comment out the PHP closing tag?
 * See: http://phpstarter.net/2009/01/omit-the-php-closing-tag/
 */
/* ?&gt; */
</pre>
<p>After running the example, you will see that we have an array with several numbered elements.  Each element is a sub-array containing an array of zones and an element containing the forecast data.</p>
<p>So there you have it.  With one request to the NWS website, you can gather all the ZFP data for an entire forecast area.  Again, this is useful when you need to gather forecasts for the general area, not just one zone at a time.  If you only need to grab one zone at a time, stay tuned because I will be writing soon on how to make use of an easier data source that grabs one zone at a time that makes for easier parsing on your part.</p>
<h3>Making it Look Pretty</h3>
<p><strong>Another teaser for a future article</strong>&#8230;Once you find the page containing the data you want to work with, it looks like an undaunted task to turn that data into something presentable.  Let me assure you right off the bat that it is doable with a healthy bit of string manipulation and regular expressions.  Just for a bit of comparison, you should be able to turn <a href="http://www.weather.gov/data/LOT/ZFPLOT">this</a> into <a href="http://chasingweather.com/forecast/lat_lon/41.9288/-87.6315#zone-forecast">this</a>.  How do we split up the days and add the cool icons?  I will cover that in the next article.  To be continued&#8230;</p>
<p>No related posts.</p>]]></content:encoded>
			<wfw:commentRss>http://phpstarter.net/2010/03/parse-zfp-zone-forecast-product-data-in-php-option-1/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>More Examples with Parsing NDFD Data in PHP</title>
		<link>http://phpstarter.net/2009/03/more-examples-with-parsing-ndfd-data-in-php/</link>
		<comments>http://phpstarter.net/2009/03/more-examples-with-parsing-ndfd-data-in-php/#comments</comments>
		<pubDate>Tue, 03 Mar 2009 11:00:10 +0000</pubDate>
		<dc:creator>Andrew</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[Advanced]]></category>
		<category><![CDATA[Weather]]></category>

		<guid isPermaLink="false">http://phpstarter.net/?p=396</guid>
		<description><![CDATA[I covered how to Parse Weather Forecast Data (from the NDFD) in PHP in a previous article, but due to the amount of questions I received, I decided to show some more usage techniques and examples. In this article, I will cover the time-series option as well as some methods on how to make some [...]
No related posts.]]></description>
			<content:encoded><![CDATA[<p>I covered how to <a href="http://phpstarter.net/2009/02/parse-weather-forecast-data-from-the-ndfd-in-php/">Parse Weather Forecast Data (from the NDFD) in PHP</a> in a previous article, but due to the amount of questions I received, I decided to show some more usage techniques and examples.  In this article, I will cover the time-series option as well as some methods on how to make some data more presentable.</p>
<p><span id="more-396"></span></p>
<h3>Fetching Time-Series Data from the NDFD</h3>
<p><em>This was covered as an appendix in the previous article, but I will include it here as well in case you are only reading it in the feeds or by email.</em></p>
<p>Requesting the time-series data returns a whole bunch more information. As requested, here is an example on how to fetch it. Change the desired parameters to ‘true’. The other examples on how to parse the time layouts and formatting data works on this XML, too.</p>
<p style="text-align: right; "><a href="/samples/396/ndfd_timeseries.php" target="_blank">Run This Example</a></p><pre name="code" class="brush: php:collapse">&lt;?php

/* http://sourceforge.net/projects/nusoap/ */
require('../includes/nusoap/nusoap.php');

$parameters = array('product'	=&gt; 'time-series',
					'latitude'  =&gt; 41.879535,
					'longitude'	=&gt; -87.624333,
					'weatherParameters' =&gt; array(
					
	'maxt' =&gt; true,			'mint' =&gt; false,		'temp' =&gt; false,			'dew' =&gt; false,	
	'appt' =&gt; true,			'pop12' =&gt; false,		'qpf' =&gt; false,				'snow' =&gt; false,	
	'sky' =&gt; false,			'rh' =&gt; false,			'wspd' =&gt; false,			'wdir' =&gt; false,	
	'wx' =&gt; false,			'icons' =&gt; false,		'waveh' =&gt; false,			'incw34' =&gt; false,	
	'incw50' =&gt; false,		'incw64' =&gt; false,		'cumw34' =&gt; false,			'cumw50' =&gt; false,	
	'cumw64' =&gt; false,		'wgust' =&gt; false,		'conhazo' =&gt; false,			'ptornado' =&gt; false,	
	'phail' =&gt; false,		'ptstmwinds' =&gt; false,	'pxtornado' =&gt; false,		'pxhail' =&gt; false,	
	'pxtstmwinds' =&gt; false,	'ptotsvrtstm' =&gt; false,	'pxtotsvrtstm' =&gt; false,	'tmpabv14d' =&gt; false,	
	'tmpblw14d' =&gt; false,	'tmpabv30d' =&gt; false,	'tmpblw30d' =&gt; false,		'tmpabv90d' =&gt; false,	
	'tmpblw90d' =&gt; false,	'prcpabv14d' =&gt; false,	'prcpblw14d' =&gt; false,		'prcpabv30d' =&gt; false,	
	'prcpblw30d' =&gt; false,	'prcpabv90d' =&gt; false,	'prcpblw90d' =&gt; false,		'precipa_r' =&gt; false,	
	'sky_r' =&gt; false,		'td_r' =&gt; false,		'temp_r' =&gt; false,			'wdir_r' =&gt; false,	
	'wwa' =&gt; false,			'wspd_r' =&gt; false)
	
					);

try
{
	/* create the nuSOAP object */
	$c = new nusoap_client('http://www.weather.gov/forecasts/xml/DWMLgen/wsdl/ndfdXML.wsdl', 'wsdl');
	
	/* make the request */
	$result = $c-&gt;call('NDFDgen', $parameters);
}
catch (Exception $ex)
{
	/* nuSOAP throws an exception is there was a problem fetching the data */
	echo 'failed';
}

header('Content-type: text/xml');
echo $result;

/* ?&gt; */
</pre>
<h3>Merging Sets of Data with Different Time Layouts</h3>
<p>The different sets of data in the NDFD are most likely going to be wanted to be displayed together.  For example, on <a href="http://chasingweather.com/forecast/lat_lon/41.3969/-87.3274#area-forecast">this page</a>, you will see that I have the conditions icons, max temps, and min temps all showing at the same time intervals.  But how is that possible?</p>
<p>In this example case, we are going to match up the high temperatures with their conditions icon.  This may not give an accurate weather condition for the day because we are going to pick the icon that is the closest to the time stamp given for that temperature.  In other words, the condition icons are for every hour or so, and the temperatures are only twice a day.  We have to pick two of those icons to match up with the two temperatures.  So for example, if the forecast for the day is &#8220;sunny in the morning, then t&#8217;storms in the afternoon&#8221;, the icon will show up as sunny.</p>
<p style="text-align: right; "><a href="/samples/396/con_times.php" target="_blank">Run This Example</a></p><pre name="code" class="brush: php:collapse">&lt;?php

/* returns the next element based on the key provided */
function array_next($array, $key, $offset = 0)
{
	/* if the key exists, we're done */
	if (isset($array[$key]) &amp;&amp; $offset == 0) return $array[$key];
	
	/* insert the key into the array and sort it */
	$array[$key] = 1;
	ksort($array);
	
	/* now get the array in order */
	$keys = array_keys($array);
	
	/* find where our inserted key is and get the next element */
	$index = array_search($key, $keys);
	if ($offset == 0) $offset = 1;
	$index += $offset;
	return (isset($keys[$index])) ? $array[$keys[$index]] : FALSE;
}

/**
 * load the forecast data array as produced in the below example:
 * http://phpstarter.net/samples/348/parse_data.php
 * More information:
 * http://phpstarter.net/2009/02/parse-weather-forecast-data-from-the-ndfd-in-php/
 */
include('parse_data.php');
$forecast = parse_data();

?&gt;
&lt;html&gt;
&lt;head&gt;&lt;title&gt;NDFD Usage Example w/ Different Time Layouts&lt;/title&gt;&lt;/head&gt;
&lt;body&gt;
&lt;h1&gt;Show High Temps with Icons&lt;/h1&gt;
&lt;?php foreach ($forecast['max_temps'] as $timestamp =&gt; $temp): ?&gt;
&lt;p&gt;&lt;?=$field?&gt;&lt;br /&gt;
&lt;img src=&quot;&lt;?=array_next($forecast['icons'], $timestamp)?&gt;&quot; /&gt;&lt;br /&gt;
High: &lt;?=$temp?&gt;&lt;/p&gt;
&lt;?php endforeach; ?&gt;
&lt;/body&gt;
&lt;/html&gt;
</pre>
<p>In the above example, the array_next() function is the one that allows us to match up the data arrays.  The first parameter is the array of the weather data that we are trying to find&#8230;or in our case, the conditions icon.  The second parameter is the key that we want to get close to&#8230;or in our case, the timestamp for the temperature we are displaying.  So, we loop through the temperatures and search for the closest icon for each one.</p>
<h3>Collecting and Using the Time Labels</h3>
<p>Depending on that kind of data you are requesting, some of the time layouts contain the time labels.  I had those time labels displayed in a data array called time_labels in a <a target="_blank" href="http://phpstarter.net/samples/348/parse_data.php">previous example</a>, but I didn&#8217;t explain how I did it.  Basically, I wrote a function that sifts through all of the time layouts.  If it finds one with a &#8220;period-name&#8221; attribute, it saves that timestamp and the period name in a data array similar to the other data items (temp, conditions, etc).</p>
<p style="text-align: right; "><a href="/samples/396/time_labels.php" target="_blank">Run This Example</a></p><pre name="code" class="brush: php:collapse">&lt;?php

/**
 * get the time layouts in an array form
 * 
 * @param string $xml
 * @return mixed
 */
function get_time_labels($xml)
{
	$data = $xml-&gt;xpath(&quot;//start-valid-time&quot;);
	$times = array();
	
	foreach ($data as $field =&gt; $value)
	{
		if ((string)$value['period-name'])
		{
			$index = (string)$value;
			$times[$index] = (string)$value['period-name'];
		}
	}
	
	ksort($times);
	
	return $times;
}

/* working with stale data, but why query for it every time */
$xml = file_get_contents('ndfd_forecast.xml');
$xml = new SimpleXMLElement($xml);

$time_labels = get_time_labels($xml);

header('Content-type: text/plain');
var_dump($time_labels);

/**
 * Why do I comment out the PHP closing tag?
 * See: http://phpstarter.net/2009/01/omit-the-php-closing-tag/
 */
/* ?&gt; */
</pre>
<p>We can apply it with the Merging Sets of Data example, and end up with something like this:</p>
<p style="text-align: right; "><a href="/samples/396/labels_conditions.php" target="_blank">Run This Example</a></p><pre name="code" class="brush: php:collapse">&lt;?php

/* returns the next element based on the key provided */
function array_next($array, $key, $offset = 0)
{
	/* if the key exists, we're done */
	if (isset($array[$key]) &amp;&amp; $offset == 0) return $array[$key];
	
	/* insert the key into the array and sort it */
	$array[$key] = 1;
	ksort($array);
	
	/* now get the array in order */
	$keys = array_keys($array);
	
	/* find where our inserted key is and get the next element */
	$index = array_search($key, $keys);
	if ($offset == 0) $offset = 1;
	$index += $offset;
	return (isset($keys[$index])) ? $array[$keys[$index]] : FALSE;
}

/**
 * load the forecast data array as produced in the below example:
 * http://phpstarter.net/samples/348/parse_data.php
 * More information:
 * http://phpstarter.net/2009/02/parse-weather-forecast-data-from-the-ndfd-in-php/
 */
include('parse_data.php');
$forecast = parse_data();

?&gt;
&lt;html&gt;
&lt;head&gt;&lt;title&gt;Show Condition Icons with Time Labels&lt;/title&gt;&lt;/head&gt;
&lt;body&gt;
&lt;h1&gt;Show Condition Icons with Time Labels&lt;/h1&gt;
&lt;?php foreach ($forecast['time_labels'] as $timestamp =&gt; $label): ?&gt;
&lt;p&gt;&lt;?=$label?&gt;&lt;br /&gt;
&lt;img src=&quot;&lt;?=array_next($forecast['icons'], $timestamp)?&gt;&quot; /&gt;&lt;/p&gt;
&lt;?php endforeach; ?&gt;
&lt;/body&gt;
&lt;/html&gt;
</pre>
<p>So there you have it &#8211; more possible applications and examples with this abundant weather forecast data.  As with all other advanced articles on phpstarter.net, this is not intended provide you with copy-and-paste code to paste right in your web applications with little or no modifications.  You really need to understand how this all works, and adapt it as necessary or rewrite the examples completely based on what you learned here and what you need to use it for.  The point is to get you to understand the concepts used here so you can <strong>apply</strong> them to your web applications.  Happy coding! <img src='http://phpstarter.net/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
<p>No related posts.</p>]]></content:encoded>
			<wfw:commentRss>http://phpstarter.net/2009/03/more-examples-with-parsing-ndfd-data-in-php/feed/</wfw:commentRss>
		<slash:comments>17</slash:comments>
		</item>
		<item>
		<title>Parse Current Weather Conditions Data from the NWS in PHP</title>
		<link>http://phpstarter.net/2009/02/parse-current-weather-conditions-data-from-the-nws-in-php/</link>
		<comments>http://phpstarter.net/2009/02/parse-current-weather-conditions-data-from-the-nws-in-php/#comments</comments>
		<pubDate>Thu, 26 Feb 2009 12:00:36 +0000</pubDate>
		<dc:creator>Andrew</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[Advanced]]></category>
		<category><![CDATA[Weather]]></category>

		<guid isPermaLink="false">http://phpstarter.net/?p=386</guid>
		<description><![CDATA[In a previous article, I covered 5 Sources of Free Weather Data for your Site, but did not provide any actual code to use the data. Last week, I covered source #1 and showed how to Parse Weather Forecast Data (from the NDFD) in PHP. For this article, I will show how to parse source [...]
No related posts.]]></description>
			<content:encoded><![CDATA[<p>In a previous article, I covered <a href="http://phpstarter.net/2008/12/5-sources-of-free-weather-data-for-your-site/">5 Sources of Free Weather Data for your Site</a>, but did not provide any actual code to use the data.  Last week, I covered source #1 and showed how to <a href="http://phpstarter.net/2009/02/parse-weather-forecast-data-from-the-ndfd-in-php/">Parse Weather Forecast Data (from the NDFD) in PHP</a>.  For this article, I will show how to parse source #2 &#8211; the current weather conditions data as provided by the National Weather Service.</p>
<p><span id="more-386"></span></p>
<h3>Fetching the Data for One Station at a Time</h3>
<p>This is ideal for a website that wants to show the current conditions for one location.  Go to the <a href="http://www.weather.gov/xml/current_obs/">XML Feeds</a> page, and find your station.  We need to know the station id for your area, so select your state, and then find the closest station to you.</p>
<p>The example below shows how easy it is to fetch the XML and format it into something we can read.</p>
<pre name="code" class="brush: php:collapse">&lt;?php

/**
 * Download a copy here:
 * http://sourceforge.net/projects/snoopy/
 */
require('../includes/Snoopy.class.php');

/**
 * Load a single station and return the data
 */
function load_single_station($station_id, &amp;$xml)
{
	$snoopy= new Snoopy();
	$snoopy-&gt;fetch('http://www.weather.gov/xml/current_obs/' . $station_id . '.xml', $xml_tmp . $station_id . '.xml');
	
	if (!$snoopy-&gt;results)
	{
		/* oops */
		return false;
	}
	
	$data = $snoopy-&gt;results;
	
	try
	{
		/* convert the XML into a data object */
		$xml = @new SimpleXMLElement($data);
		/* convert that data object into an array, and return it */
		return get_object_vars($xml);
	}
	catch (Exception $e)
	{
		/* we got an empty or invalid XML file */
		return false;
	}
}

$conditions = load_single_station('KLOT', $xml);
var_dump($conditions);

/**
 * Why do I comment out the PHP closing tag?
 * See: http://phpstarter.net/2009/01/omit-the-php-closing-tag/
 */
/* ?&gt; */
</pre>
<p>Although that example gets the job done, we are going to want to do some caching.  It&#8217;s a bit pointless to be fetching that XML file every time a page is requested.  With a little bit of database code, we can make it relatively easy.  First, setup a database that your script has access to, and create this table:</p>
<pre class="brush: sql">
CREATE TABLE `Current` (
  `suggested_pickup` varchar(55) NOT NULL,
  `last_update` timestamp NOT NULL default CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP,
  `suggested_pickup_period` smallint(6) NOT NULL,
  `location` varchar(55) NOT NULL,
  `station_id` varchar(10) NOT NULL,
  `latitude` decimal(5,2) NOT NULL,
  `longitude` decimal(5,2) NOT NULL,
  `observation_time` varchar(255) NOT NULL,
  `observation_time_rfc822` varchar(255) NOT NULL,
  `weather` varchar(55) NOT NULL,
  `temperature_string` varchar(55) NOT NULL,
  `temp_f` smallint(6) NOT NULL,
  `temp_c` smallint(6) NOT NULL,
  `relative_humidity` tinyint(4) NOT NULL,
  `wind_string` varchar(55) NOT NULL,
  `wind_dir` varchar(55) NOT NULL,
  `wind_degrees` smallint(6) NOT NULL,
  `wind_mph` smallint(6) NOT NULL,
  `wind_gust_mph` smallint(6) NOT NULL,
  `pressure_string` varchar(55) NOT NULL,
  `pressure_mb` smallint(6) NOT NULL,
  `pressure_in` decimal(5,2) NOT NULL,
  `dewpoint_string` varchar(55) NOT NULL,
  `dewpoint_f` smallint(6) NOT NULL,
  `dewpoint_c` smallint(6) NOT NULL,
  `heat_index_string` varchar(55) NOT NULL,
  `heat_index_f` smallint(6) NOT NULL,
  `heat_index_c` smallint(6) NOT NULL,
  `windchill_string` varchar(55) NOT NULL,
  `windchill_f` smallint(6) NOT NULL,
  `windchill_c` smallint(6) NOT NULL,
  `visibility_mi` decimal(5,2) NOT NULL,
  `icon_url_base` varchar(255) NOT NULL,
  `icon_url_name` varchar(55) NOT NULL,
  `two_day_history_url` varchar(255) NOT NULL,
  `ob_url` varchar(255) NOT NULL,
  PRIMARY KEY  (`station_id`),
  KEY `latitude` (`latitude`,`longitude`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
</pre>
<p>After that MySQL table is created, have a look at this next example.  It&#8217;s the same as the last one, but with two new functions: load_single_station_cache() &#038; save_conditions().  The load_single_station_cache() function checks the table for a non-stale record for the specified station.  If it finds one, it returns that database record, and no XML file is downloaded.  If it doesn&#8217;t find one, it calls up load_single_station(), just like the first example, and then saves that data with the save_conditions() function.</p>
<p style="text-align: right; "><a href="/samples/386/single_station_cache.php" target="_blank">Run This Example</a></p><pre name="code" class="brush: php:collapse">&lt;?php

/**
 * Download a copy here:
 * http://sourceforge.net/projects/snoopy/
 */
require('../includes/Snoopy.class.php');

/**
 * replace with your database connection code
 */
require('../includes/database.php');
db_connect();

/**
 * Load a single station and return the data
 */
function load_single_station($station_id, &amp;$xml)
{
	$snoopy= new Snoopy();
	$snoopy-&gt;fetch('http://www.weather.gov/xml/current_obs/' . $station_id . '.xml', $xml_tmp . $station_id . '.xml');
	
	if (!$snoopy-&gt;results)
	{
		/* oops */
		return false;
	}
	
	$data = $snoopy-&gt;results;
	
	try
	{
		/* convert the XML into a data object */
		$xml = @new SimpleXMLElement($data);
		/* convert that data object into an array, and return it */
		return get_object_vars($xml);
	}
	catch (Exception $e)
	{
		/* we got an empty or invalid XML file */
		return false;
	}
}

/**
 * Save the weather conditions to the database table.
 */
function save_conditions($conditions)
{
	/**
	 * Get the columns and interect that list with the data array, 
	 * so we don't try to insert some fields that don't exist
	 */
	$query = &quot;SHOW COLUMNS FROM Current&quot;;
	$result = mysql_query($query);
	$fields = array();
	
	for ($i = 0, $n = mysql_num_rows($result); $i &lt; $n; $i++)
	{
		$row = mysql_fetch_row($result);
		$fields[] = $row[0];
	}
	$fields = array_flip($fields);
	$conditions = array_intersect_key($conditions, $fields);
	
	/**
	 * Form the data pairs into query format.
	 * Uncomment the echo statement below to see what it's doing.
	 */
	$fields = $values = '';
	foreach ($conditions as $field =&gt; $value)
	{
		$fields .= $field . &quot;,&quot;;
		$values .= &quot;'&quot; . mysql_real_escape_string($value) . &quot;',&quot;;
	}

	/* remove the last comma from both variables */
	$fields = substr($fields, 0, strlen($fields) - 1);
	$values = substr($values, 0, strlen($values) - 1);
	
	$query = &quot;DELETE FROM Current WHERE station_id = '{$conditions['station_id']}' LIMIT 1&quot;;
	mysql_query($query);
	$query = &quot;INSERT INTO Current ($fields) VALUES ($values)&quot;;
	//echo $query;
	$result = mysql_query($query);
	if (!$result) die(mysql_error());
}

/**
 * Load the weather conditions from the DB table, and make the necessary 
 * calls if a recent record does not exist.
 */
function load_single_station_cache($station_id, &amp;$xml)
{
	$station_id_esc = mysql_real_escape_string($station_id);
	
	/* no results will be returned if there is no record, *or* if 
		the record is more than 5400 seconds (90 minutes) old */
	$query = &quot;SELECT * FROM Current WHERE station_id = '$station_id_esc' &amp;&amp; last_update + 5400 &gt; NOW() LIMIT 1&quot;;
	$result = mysql_query($query);
	if (!$result) die(mysql_error());
	
	if (mysql_num_rows($result) == 0)
	{
		/* load a fresh set of conditions */
		$conditions = load_single_station($station_id, &amp;$xml);
		
		/* don't forget to save it for later */
		save_conditions($conditions);
		
		return $conditions;
	}
	else
	{
		/* we got it */
		return mysql_fetch_assoc($result);
	}
}

$conditions = load_single_station_cache('KLOT', $xml);
header('Content-type: text/plain');
var_dump($conditions);

/**
 * Why do I comment out the PHP closing tag?
 * See: http://phpstarter.net/2009/01/omit-the-php-closing-tag/
 */
/* ?&gt; */
</pre>
<h3>Fetching the Data for all Stations</h3>
<p>Higher traffic sites that show conditions for all or most of the available stations may want to download all of the stations at once from their <a href="http://www.weather.gov/xml/current_obs/">data feeds page</a>, and import them into a database.  To do this, we are going to need to download the zip file from the website, and then extract it.  I am not going to provide specific code, because it&#8217;s unique to the server environment.  Binary files can be downloaded via <a href="http://us.php.net/manual/en/book.curl.php">cURL</a> or <a href="http://us.php.net/manual/en/function.fsockopen.php">fsockopen()</a> as an alternative.  Once downloaded, use PHP&#8217;s <a href="http://us2.php.net/zip">Zip functions</a> to extract the files to a temp directory.  <a href="http://www.zlib.net/">Zlib</a> is required to open a zip archive in PHP, so if you don&#8217;t have it, you can use the <a href="http://us.php.net/manual/en/function.shell-exec.php">shell_exec()</a> function to run the necessary unzip functions in the shell, assuming that your server supports it.</p>
<p>Once you have the XML files at your disposal, use something like the <a href="http://us2.php.net/manual/en/function.scandir.php">scandir()</a> function and import all files using something like the single-station example.</p>
<p>So there you have it &#8211; XML files turned into an easy to manage, cache-able associative array.</p>
<div class="m_info">If these examples were enough to make your head spin, there are easy alternatives to show current conditions on your website.  Check out these <a href="http://wiki.wunderground.com/index.php/Weather_Stickers">free weather stickers</a> that are easier to use, although not as customizable.</div>
<p>No related posts.</p>]]></content:encoded>
			<wfw:commentRss>http://phpstarter.net/2009/02/parse-current-weather-conditions-data-from-the-nws-in-php/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Parse Weather Forecast Data (from the NDFD) in PHP</title>
		<link>http://phpstarter.net/2009/02/parse-weather-forecast-data-from-the-ndfd-in-php/</link>
		<comments>http://phpstarter.net/2009/02/parse-weather-forecast-data-from-the-ndfd-in-php/#comments</comments>
		<pubDate>Thu, 19 Feb 2009 12:00:44 +0000</pubDate>
		<dc:creator>Andrew</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[Advanced]]></category>
		<category><![CDATA[Weather]]></category>

		<guid isPermaLink="false">http://phpstarter.net/?p=348</guid>
		<description><![CDATA[In a previous article, I covered 5 Sources of Free Weather Data for your Site, but did not provide any actual code to use the data. Starting with this article, I will post instructions on how to handle this data as well as sample code. For this article, we will start with the National Digital [...]
No related posts.]]></description>
			<content:encoded><![CDATA[<p>In a previous article, I covered <a href="http://phpstarter.net/2008/12/5-sources-of-free-weather-data-for-your-site/">5 Sources of Free Weather Data for your Site</a>, but did not provide any actual code to use the data.  Starting with this article, I will post instructions on how to handle this data as well as sample code.  For this article, we will start with the National Digital Forecast Database (NDFD) Simple Object Access Protocol (SOAP) Web Service.</p>
<p><span id="more-348"></span></p>
<h3>Where to Find the Data</h3>
<p>See <a href="http://www.weather.gov/xml">weather.gov/xml</a> for full details and available data.  We will be using the 24 Hourly format, which gives us temperatures, cloud cover %, weather type (rain, snow, etc), hazardous conditions, and even weather icons.</p>
<h3>Code to Fetch the Data</h3>
<p>Below is some sample code that will fetch the data.  The lat/lon coordinates need to be changed for your desired location on lines 10-11.  Currently, it is set for Chicago, IL.</p>
<pre name="code" class="brush: php">&lt;?php

/* http://sourceforge.net/projects/nusoap/ */
require('../includes/nusoap/nusoap.php');

$parameters = array('product'	=&gt; 'glance',
					'numDays'   =&gt; 5,
					'format'    =&gt; '24 hourly',
					'latitude'  =&gt; 41.879535,
					'longitude'	=&gt; -87.624333);

try
{
	/* create the nuSOAP object */
	$c = new nusoap_client('http://www.weather.gov/forecasts/xml/DWMLgen/wsdl/ndfdXML.wsdl', 'wsdl');
	
	/* make the request */
	$result = $c-&gt;call('NDFDgen', $parameters);
}
catch (Exception $ex)
{
	/* nuSOAP throws an exception is there was a problem fetching the data */
	echo 'failed';
}

header('Content-type: text/xml');
echo $result;

/* ?&gt; */
</pre>
<p>This is by no means a production sample.  You are going to want to cache the results somehow so you aren&#8217;t querying the NDFD every time you need this information.  It is only updated every hour at most, so there is no need to query it more frequent than that.</p>
<h3>Understanding the XML Output</h3>
<p>It took me a great deal of staring and reading to figure out how this XML output is organized.  Have a look at this <a href="/samples/348/ndfd_forecast.xml" target="_blank">sample output</a>, as I will refer to this specific example to explain the sections.  I recommend loading the sample XML file in Firefox or any other web browser that allows you to collapse and expand the XML tree.</p>
<p>First, collapse the &#8220;head&#8221; area by clicking on the minus next to the &#8220;head&#8221; tag.  Everything in there is pretty self-explanatory.  Our focus is going to be in the &#8220;data&#8221; tag.  Next, collapse all tags inside the &#8220;data&#8221; tag, except for the &#8220;parameters&#8221; tag, but collapse everything inside the &#8220;parameters&#8221; tag so we can see everything in there w/o scrolling.  Here is what your view should look like:</p>
<p><a href="http://phpstarter.net/wp-content/uploads/2009/02/ndfd_01.png"><img src="http://phpstarter.net/wp-content/uploads/2009/02/ndfd_01-590x261.png" alt="ndfd_01" title="ndfd_01" width="590" height="261" class="alignnone size-large wp-image-352" /></a></p>
<p>Now we are at a point where we can see how this is all grouped together.  As we can see, the &#8220;parameters&#8221; section shows the products that are available, which are temperature (minimum), temperature (maximum), cloud-amount, weather, conditions-icon, and hazards.  Each of them is associated with a time layout because most products are on a different time scale.  For example, max temps are during the afternoon &#038; once a day, min temps are at night &#038; once a day, the conditions icons are at several points throughout the next several days, etc.  It is separated out this way because not all products have a different time scale &#8211; some are the same, like &#8220;weather&#8221; and &#8220;conditions-icon&#8221; in our example.</p>
<p>Time to match each product to their time layout.  We see that the maximum temps have the time layout of &#8220;k-p24h-n7-1&#8243;, so we go up to the time layouts and find one with the layout-key of &#8220;k-p24h-n7-1&#8243;.</p>
<p><a href="http://phpstarter.net/wp-content/uploads/2009/02/ndfd_02.png"><img src="http://phpstarter.net/wp-content/uploads/2009/02/ndfd_02-590x418.png" alt="ndfd_02" title="ndfd_02" width="590" height="418" class="alignnone size-large wp-image-354" /></a></p>
<p>We have to code a way to merge all data sets with their corresponding time layout.  This may look complicated, but I will show you a somewhat easy way to parse this in PHP.</p>
<p style="text-align: right; "><a href="/samples/348/parse_times.php" target="_blank">Run This Example</a></p><pre name="code" class="brush: php:collapse">&lt;?php
/************************************/
/* some functions we will use later */
/************************************/

/**
 * get the string values of the object items
 * @param mixed $object the XML object to extract the string values from
 * @return array
 */
function get_values($object)
{
	$arr = array();
	foreach ($object as $field =&gt; $value)
	{
		$arr[] = (string)$value;
	}
	
	return $arr;
}

/*****************************/
/* actual script starts here */
/*****************************/

/* load our example from http://phpstarter.net/samples/348/ndfd_forecast.xml */
$xml_data = file_get_contents('ndfd_forecast.xml');

/* parse the XML data into a giant data object */
try
{
	$xml = new SimpleXMLElement($xml_data);
}
catch (Exception $ex)
{
	/* the XML was probably invalid */
	die('Failed to parse the XML');
}

/* all time layouts go in here with the layout-key as the array keys */
$times = array();

/* loop through the time-layouts in the XML */
foreach ($xml-&gt;xpath('//time-layout') as $field =&gt; $value)
{
	/* get the layout-key to make it the array key value */
	$layout = (string)$value-&gt;{'layout-key'};
	
	
	$times[$layout] = array('start' =&gt; get_values($value-&gt;xpath('start-valid-time')), 
							'end' =&gt; get_values($value-&gt;xpath('end-valid-time')));
}

header('Content-type: text/plain');
var_dump($times);


/**
 * Why do I comment out the PHP closing tag?
 * See: http://phpstarter.net/2009/01/omit-the-php-closing-tag/
 */
/* ?&gt; */
</pre>
<p>Now we have all the time layouts in a way we can manage it.  The next task is to match it up to the data to their respective time layouts.  This next example includes the code to organize the time layouts, and then organize the data to correspond with the times.  Running the example below will show that the data is now neatly organized in a way that it can be easily used.</p>
<p style="text-align: right; "><a href="/samples/348/parse_data.php" target="_blank">Run This Example</a></p><pre name="code" class="brush: php:collapse">&lt;?php
/************************************/
/* some functions we will use later */
/************************************/

/**
 * get the string values of the object items
 * @param mixed $object the XML object to extract the string values from
 * @return array
 */
function get_values($object)
{
	$arr = array();
	foreach ($object as $field =&gt; $value)
	{
		$arr[] = (string)$value;
	}
	
	return $arr;
}

/**
 * Combine a time layout with a product
 * 
 * @param string $data_xpath the xpath to the data set
 * @param string $time_xpath the xpath to the time layout
 * @param array $times the big assoc array of all the time layouts
 */
function merge_times_data($data_xpath, $time_xpath, $times, $xml)
{
	$data = get_values($xml-&gt;xpath($data_xpath));
	$time_layout = $xml-&gt;xpath($time_xpath);
	if (!$time_layout) return false;
	$time_layout = (string)$time_layout[0]['time-layout'];
	$data = array_combine($times[$time_layout]['start'], $data);
	
	return $data;
}

/**
 * get the time layouts in an array form
 * 
 * @param string $xml
 * @return mixed
 */
function get_time_labels($xml)
{
	$data = $xml-&gt;xpath(&quot;//start-valid-time&quot;);
	$times = array();
	
	foreach ($data as $field =&gt; $value)
	{
		if ((string)$value['period-name'])
		{
			$index = (string)$value;
			$times[$index] = (string)$value['period-name'];
		}
	}
	
	ksort($times);
	
	return $times;
}

/*****************************/
/* actual script starts here */
/*****************************/

/* load our example from http://phpstarter.net/samples/348/ndfd_forecast.xml */
$xml_data = file_get_contents('ndfd_forecast.xml');

/* parse the XML data into a giant data object */
try
{
	$xml = new SimpleXMLElement($xml_data);
}
catch (Exception $ex)
{
	/* the XML was probably invalid */
	die('Failed to parse the XML');
}

/* all time layouts go in here with the layout-key as the array keys */
$times = array();

/* loop through the time-layouts */
foreach ($xml-&gt;xpath('//time-layout') as $field =&gt; $value)
{
	/* get the layout-key to make it the array key value */
	$layout = (string)$value-&gt;{'layout-key'};
	
	
	$times[$layout] = array('start' =&gt; get_values($value-&gt;xpath('start-valid-time')), 
							'end' =&gt; get_values($value-&gt;xpath('end-valid-time')));
}

$forecast['max_temps'] = merge_times_data(&quot;//parameters/temperature[@type='maximum']/value&quot;, 
	&quot;//parameters/temperature[@type='maximum']&quot;, $times, $xml);
$forecast['min_temps'] = merge_times_data(&quot;//parameters/temperature[@type='minimum']/value&quot;, 
	&quot;//parameters/temperature[@type='minimum']&quot;, $times, $xml);
$forecast['temps'] = array_merge($forecast['min_temps'], $forecast['max_temps']);
$forecast['icons'] = merge_times_data(&quot;//parameters/conditions-icon/icon-link&quot;, 
	&quot;//parameters/conditions-icon&quot;, $times, $xml);
$forecast['time_labels'] = get_time_labels($xml);

header('Content-type: text/plain');
var_dump($forecast);

/**
 * Why do I comment out the PHP closing tag?
 * See: http://phpstarter.net/2009/01/omit-the-php-closing-tag/
 */
/* ?&gt; */
</pre>
<p>There is another function in that example that associates the times to the time labels that you see in the XML.  Some of them has a parameter called &#8220;period-name&#8221;, which is the day of the week or holiday name, if applicable.</p>
<h3>How to Use the Formatted Data</h3>
<p>For this last example, I will show you in the simplest terms how to use this data.  Remember, we are not pulling data from the NDFD live &#8211; this is using the XML from our <a href="/samples/348/ndfd_forecast.xml" target="_blank">earlier example</a>, so the temps are for mid-winter in NW Indiana.</p>
<p style="text-align: right; "><a href="/samples/348/ndfd_usage.php" target="_blank">Run This Example</a></p><pre name="code" class="brush: php:collapse">&lt;?php

/**
 * pick up where we left off from the last example
 * no need to generate the data array again here
 */
$forecast = file_get_contents('data.txt');
$forecast = unserialize($forecast);

/**
 * Show the temperatures
 */

foreach ($forecast['max_temps'] as $field =&gt; $value)
{
	echo 'High temp for ' . $forecast['time_labels'][$field] . ': ' . $value . &quot;&lt;br /&gt;\n&quot;;
}

foreach ($forecast['min_temps'] as $field =&gt; $value)
{
	echo 'Low temp for ' . $forecast['time_labels'][$field] . ': ' . $value . &quot;&lt;br /&gt;\n&quot;;
}

/**
 * Why do I comment out the PHP closing tag?
 * See: http://phpstarter.net/2009/01/omit-the-php-closing-tag/
 */
/* ?&gt; */
</pre>
<p>So there you have it &#8211; an XML parsing nightmare made easy.  This method is in my opinion the best way to receive and parse this data from the National Weather Service.  It may not be the best way for everyone, so if you have something better to add, please post a comment!</p>
<h3>(Added 2009/02/20) Fetching Time-Series Data from the NDFD</h3>
<p>Requesting the time-series data returns a whole bunch more information.  As requested, here is an example on how to fetch it.  Change the desired parameters to &#8216;true&#8217;.  The other examples on how to parse the time layouts and formatting data works on this XML, too.</p>
<pre name="code" class="brush: php:collapse">&lt;?php

/* http://sourceforge.net/projects/nusoap/ */
require('../includes/nusoap/nusoap.php');

$parameters = array('product'	=&gt; 'time-series',
					'latitude'  =&gt; 41.879535,
					'longitude'	=&gt; -87.624333,
					'weatherParameters' =&gt; array(
					
	'maxt' =&gt; true,			'mint' =&gt; false,		'temp' =&gt; false,			'dew' =&gt; false,	
	'appt' =&gt; true,			'pop12' =&gt; false,		'qpf' =&gt; false,				'snow' =&gt; false,	
	'sky' =&gt; false,			'rh' =&gt; false,			'wspd' =&gt; false,			'wdir' =&gt; false,	
	'wx' =&gt; false,			'icons' =&gt; false,		'waveh' =&gt; false,			'incw34' =&gt; false,	
	'incw50' =&gt; false,		'incw64' =&gt; false,		'cumw34' =&gt; false,			'cumw50' =&gt; false,	
	'cumw64' =&gt; false,		'wgust' =&gt; false,		'conhazo' =&gt; false,			'ptornado' =&gt; false,	
	'phail' =&gt; false,		'ptstmwinds' =&gt; false,	'pxtornado' =&gt; false,		'pxhail' =&gt; false,	
	'pxtstmwinds' =&gt; false,	'ptotsvrtstm' =&gt; false,	'pxtotsvrtstm' =&gt; false,	'tmpabv14d' =&gt; false,	
	'tmpblw14d' =&gt; false,	'tmpabv30d' =&gt; false,	'tmpblw30d' =&gt; false,		'tmpabv90d' =&gt; false,	
	'tmpblw90d' =&gt; false,	'prcpabv14d' =&gt; false,	'prcpblw14d' =&gt; false,		'prcpabv30d' =&gt; false,	
	'prcpblw30d' =&gt; false,	'prcpabv90d' =&gt; false,	'prcpblw90d' =&gt; false,		'precipa_r' =&gt; false,	
	'sky_r' =&gt; false,		'td_r' =&gt; false,		'temp_r' =&gt; false,			'wdir_r' =&gt; false,	
	'wwa' =&gt; false,			'wspd_r' =&gt; false)
	
					);

try
{
	/* create the nuSOAP object */
	$c = new nusoap_client('http://www.weather.gov/forecasts/xml/DWMLgen/wsdl/ndfdXML.wsdl', 'wsdl');
	
	/* make the request */
	$result = $c-&gt;call('NDFDgen', $parameters);
}
catch (Exception $ex)
{
	/* nuSOAP throws an exception is there was a problem fetching the data */
	echo 'failed';
}

header('Content-type: text/xml');
echo $result;

/* ?&gt; */
</pre>
<p>More tips can now be found here: <a href="http://phpstarter.net/2009/03/more-examples-with-parsing-ndfd-data-in-php/">More Examples with Parsing NDFD Data in PHP</a></p>
<p>No related posts.</p>]]></content:encoded>
			<wfw:commentRss>http://phpstarter.net/2009/02/parse-weather-forecast-data-from-the-ndfd-in-php/feed/</wfw:commentRss>
		<slash:comments>35</slash:comments>
		</item>
		<item>
		<title>Get the Lat/Lon Location of Major US Cities</title>
		<link>http://phpstarter.net/2009/01/get-the-latlon-location-of-major-us-cities/</link>
		<comments>http://phpstarter.net/2009/01/get-the-latlon-location-of-major-us-cities/#comments</comments>
		<pubDate>Tue, 27 Jan 2009 12:00:55 +0000</pubDate>
		<dc:creator>Andrew</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[Advanced]]></category>
		<category><![CDATA[Weather]]></category>

		<guid isPermaLink="false">http://phpstarter.net/?p=310</guid>
		<description><![CDATA[In this article, learn how to fetch the latitude &#038; longitude location of all major US cities. I&#8217;m going to provide you with a comprehensive list of almost 500 major US cities as well as the source, so you can get the updated list as city sizes change. This data is provided by the National [...]
No related posts.]]></description>
			<content:encoded><![CDATA[<p>In this article, learn how to fetch the latitude &#038; longitude location of all major US cities.  I&#8217;m going to provide you with a comprehensive list of almost 500 major US cities as well as the source, so you can get the updated list as city sizes change.</p>
<p><span id="more-310"></span></p>
<p>This data is provided by the National Digital Forecast Database (NDFD).  They supply the city data in groups, or all at once, so you won&#8217;t be making calls to this service often, as city locations don&#8217;t normally change.  As a result, you can skip the task of writing XML parsing and import scripts, and just import the data directly from here.  I have prepared a SQL import file straight from this data with no alterations.</p>
<p><strong>City Location Data:</strong> <a href="http://phpstarter.net/wp-content/uploads/2009/01/citynames.sql">SQL</a> | <a href="http://phpstarter.net/wp-content/uploads/2009/01/citynames.csv">CSV</a></p>
<p>This data is retrieved by calling the LatLonListCityNames() function from the <a href="http://www.weather.gov/xml/">NDFD</a>.  The National Weather Service presents the cities in 4 different levels.  More information can be found by viewing their <a href="http://www.weather.gov/xml/">NDFD</a> page and scroll down to the the section that describes the LatLonListCityNames() function.</p>
<h3>Example: Query the Database for a City Name</h3>
<p>The following code is the best way to find the closest city for the lat/lon provided.</p>
<p style="text-align: right; "><a href="/samples/310/find_cities.php" target="_blank">Run This Example</a></p><pre name="code" class="brush: php:collapse">&lt;?php

/**
 * This is how I connect to my database - adjust as necessary
 */
include('../includes/database.php');
$db_link = db_connect();

function get_city_names($lat, $lon, $count = 1)
{
	global $db_link;
	$lat = (float)$lat;
	$lon = (float)$lon;
	
	/**
	 * this query does all the work - with a little math, 
	 * we can search by cloest results
	 */
	$query = &quot;SELECT *, SQRT(POW(69.1 * (lat - $lat), 2) + POW(69.1 * 
		($lon - lon) *  COS(lat / 57.3 ), 2 )) AS distance FROM CityNames 
		ORDER BY distance ASC LIMIT $count&quot;;
	$result = mysql_query($query, $db_link);
	
	/**
	 * no need for a multi-dimensial array if there is 
	 * only one result
	 */
	if (mysql_num_rows($result) == 1)
	{
		return mysql_fetch_assoc($result);
	}
	else
	{
		$rows = array();
		for ($i = 0, $c = mysql_num_rows($result); $i &lt; $c; $i++)
		{
			$rows[] = mysql_fetch_assoc($result);
		}
	}
	
	return $rows;
}

/**
 * Get the 5 closest cities to the given coordinates
 */
$cities = get_city_names(41.9224, -87.6524, 5);

header('Content-type: text/plain');
var_dump($cities);

/**
 * Why do I comment out my PHP closing tag?  It's actually not needed!
 * See: http://phpstarter.net/2009/01/omit-the-php-closing-tag/
 */

/* ?&gt; */
</pre>
<h3>Example: Fetching the City Names from the NDFD</h3>
<p>You do not need to know this if you use the SQL import file offered above.  If you want to know how this data was retrieved, here is an example.  It requires a copy of <a href="http://sourceforge.net/projects/nusoap/">NuSOAP</a>, so download yourself a copy if you don&#8217;t have it.</p>
<pre name="code" class="brush: php:collapse">&lt;?php

/**
 * Download NuSOAP from the link below and change the require path accordingly
 * http://sourceforge.net/projects/nusoap/
 */
require('../includes/nusoap/nusoap.php');

/**
 * Fetch the list of cities &amp; location from the NDFD
 * 
 * @param int $level the city level to import (http://www.weather.gov/xml/)
 * @return mixed
 */
function import_city_names($level = 1)
{
	$level = (int)$level;
	
	/**
	 * SOAP parameters go here.  We only need to send the city level
	 */
	$parameters = array('displayLevel' =&gt; $level);
	
	/**
	 * couple things can go wrong here; the soap request can fail, or we can 
	 * be sent an invalid XML file, which would cause problems
	 */
	try
	{
		$c = new nusoap_client('http://www.weather.gov/forecasts/xml/DWMLgen/wsdl/ndfdXML.wsdl', 'wsdl');
		
		$result = $c-&gt;call('LatLonListCityNames', $parameters, '', '', false, true);
		
		$xml = new SimpleXMLElement($c-&gt;return['listLatLonOut']);
	}
	catch (Exception $e)
	{
		/* fail! */
		return false;
	}
	
	/**
	 * parse through the XML fields
	 * run var_dump($xml) to see how the XML data fields are formed
	 */
	$city_name_list = explode('|', $xml-&gt;cityNameList);
	$lat_lon_list = explode(' ', $xml-&gt;latLonList);
	
	$data = array();
	for ($i = 0, $c = count($city_name_list); $i &lt; $c; $i++)
	{
		$row = array();
		list($row['city'], $row['state']) = explode(',', array_pop($city_name_list));
		list($row['lat'], $row['lon']) = explode(',', array_pop($lat_lon_list));
		
		$data[] = $row;
	}
	
	return $data;
}

/* run the function to list all level 1 cities */
$cities = import_city_names(1);

/**
 * For demonstration purposes, we are just going to dump the data.
 * If this were a production script, we would be putting it in storage (database, etc)
 */
var_dump($cities);

/**
 * Why do I comment out my PHP closing tag?  It's actually not needed!
 * See: http://phpstarter.net/2009/01/omit-the-php-closing-tag/
 */

/* ?&gt; */
</pre>
<p class="m_warning">Do not use this function every time you need to lookup a city.  This information is rarely updated, so it&#8217;s best to load it into a database or flat file for storage and retrieval as necessary.</p>
<p>I used this source to search for a weather forecast by city.  This data isn&#8217;t perfect as it doesn&#8217;t cover every town and village, but it covers enough for my purposes.  If you know of a free data source that gets more specific, please share in a comment.</p>
<p>No related posts.</p>]]></content:encoded>
			<wfw:commentRss>http://phpstarter.net/2009/01/get-the-latlon-location-of-major-us-cities/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>5 Sources of Free Weather Data for your Site</title>
		<link>http://phpstarter.net/2008/12/5-sources-of-free-weather-data-for-your-site/</link>
		<comments>http://phpstarter.net/2008/12/5-sources-of-free-weather-data-for-your-site/#comments</comments>
		<pubDate>Thu, 11 Dec 2008 14:00:11 +0000</pubDate>
		<dc:creator>Andrew</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[Advanced]]></category>
		<category><![CDATA[Weather]]></category>

		<guid isPermaLink="false">http://phpstarter.net/?p=91</guid>
		<description><![CDATA[The weather has always fascinated me, and I actually almost became a meteorologist instead of a full-time entrepreneur. I recently had a project where I had to pull tons of weather data and display it on a website. It took tons of research to find what I was looking for, so I am presenting all [...]
No related posts.]]></description>
			<content:encoded><![CDATA[<p>The weather has always fascinated me, and I actually almost became a meteorologist instead of a full-time entrepreneur.  I recently had a project where I had to pull tons of weather data and display it on a website.  It took tons of research to find what I was looking for, so I am presenting all my findings here 1) for my own reference, and 2) for your convenience.</p>
<p><span id="more-91"></span></p>
<p class="m_warning">This article requires in-depth know-how of parsing through vast amounts of XML data and text.  If you are looking for ways to display weather information on your website w/o knowing any of this stuff, wunderground.com has plenty of <a href="http://wiki.wunderground.com/index.php/Weather_Stickers">weather sticker designs</a> that you can add to your site very easily.</p>
<h3>Source #1: National Digital Forecast Database (NDFD) Simple Object Access Protocol (SOAP) Web Service</h3>
<p>This source is your best bet for free, public domain, information to display on your website, desktop weather tool, etc.  This is going straight to the source, which is the National Weather Service.  I always recommend going to them first before some of the commercial sites, like Weather.com or Accuweather, because they get most of their weather data from the NWS anyway.  This data feed provides virtually all the raw data needed to form a forecast.  It doesn&#8217;t include the english-formed verbage you might be used to reading, but it does provide all sorts of data from high temps, to precip chances, to risk of tornadoes.</p>
<p>You can send your request via SOAP, which is the recommended way of doing it.  I would recommend using <a href="http://sourceforge.net/projects/nusoap/">nuSOAP</a>, but you are free to use whatever you like best.  There is also a <a href="http://us3.php.net/manual/en/book.soap.php">native PHP SOAP library</a>.  The NWS also offers the ability to retrieve the information by calling a simple URL with a query string.</p>
<p><strong>Note:</strong> You can only query for a forecast by providing the ZIP code or lat/lon pairs, however, the NDFD also provides the ability to query for city names &#038; zip codes and get the lat/lon pairs in return.  See this <a href="http://www.weather.gov/forecasts/xml/SOAP_server/ndfdXML.htm">SOAP Response Generator</a> for more information and also the ability to get real responses from the server.</p>
<p>All data is received back in XML format, so you need to have a good grip on parsing XML data.</p>
<p>View the data source here: <a href="http://www.weather.gov/xml/">http://www.weather.gov/xml/</a></p>
<p>Sample code and usage instructions now available here:<br />
<a href="http://phpstarter.net/2009/02/parse-weather-forecast-data-from-the-ndfd-in-php/">Parse Weather Forecast Data (from the NDFD) in PHP</a><br />
<a href="http://phpstarter.net/2009/03/more-examples-with-parsing-ndfd-data-in-php/">More Examples with Parsing NDFD Data in PHP</a></p>
<h3>Source #2: XML Feeds of Current Weather Conditions</h3>
<p>Another data feed service provided by the NWS, this product provides current conditions from all NWS weather stations.  If the current conditions are desired for only a few weather stations, they can be fetched individually.  The option is also available to download all current conditions in one zip file.</p>
<p>All data is, again, received in XML format, so you need to have a good grip on how to process XML data.  The zip file contains the XML file for every weather station, so some programming is needed to extract the file and import each and every XML file.</p>
<p>View the data source here: <a href="http://www.weather.gov/xml/current_obs/">http://www.weather.gov/xml/current_obs/</a></p>
<p>Sample code and usage instructions now available here:<br />
<a href="http://phpstarter.net/2009/02/parse-current-weather-conditions-data-from-the-nws-in-php/">Parse Current Weather Conditions Data from the NWS in PHP</a></p>
<h3>Source #3: Weather.gov/data</h3>
<p>This last data source from the national weather service is not well documented, and is very difficult to integrate into any software.  If you are looking for those English forecasts that read something like &#8220;<em>Monday&#8230;Mostly cloudy with a 40 percent chance of light snow. Highs around 30.</em>&#8220;, then you need to parse this next data source by hand.  We don&#8217;t have the luxury of XML here.</p>
<p>Have a look at <a href="http://www.weather.gov/data/LOT/ZFPLOT">this link</a>.  This is what they call the &#8220;Zone Forecast Product&#8221; for my local weather station.  In order to get the ZFP for your station area, you need to load http://www.weather.gov/data/XXX/ZFPXXX, where XXX is the three-letter code for your station.  As you can see, there are several forecasts on that page.  That&#8217;s because there are many zones in one station area.  It&#8217;s up to you to find out which one you want.</p>
<p>To understand the zone locations for these forecasts, you need to study the <a href="http://www.weather.gov/emwin/winugc.htm">Universal Geographic Code</a> specification very carefully.  You must parse that cryptic UGC line that&#8217;s at the top of every forecast on the ZFP page.  If you want to know the locations of each zone, see the <a href="http://www.weather.gov/geodata/catalog/wsom/html/cntyzone.htm">County-Public Forecast Zones Correlation File</a>.</p>
<p>If you&#8217;re curious what other data you can mine through, besides the zone forecast data, browse <a href="http://www.weather.gov/data/">http://www.weather.gov/data/</a>.</p>
<h3>Source #4: Weather Underground XML API</h3>
<p>Our next data feed is from a non-government organization.  Their Wiki now says that their data is free of charge for &#8220;personal, non-commercial purposes&#8221;.  If you are using their data for commercial or for-profit sites, be sure to contact them for licensing.  If you are not using this data for profit, it&#8217;s one of the easiest data sources to use.  All you need to know is how to parse XML and fetch remote pages.</p>
<p>You can&#8217;t download current conditions or forecasts in bulk, but you can query by zip code, airport code, or city.  With using the NWS data you have to use the zip code, lat lon, zone code, or station number.</p>
<p>Wunderground does provide something that the National Weather Service does not.  Wunderground has a vast network of personal weather stations and webcams.  So if you query for current conditions, you get the NWS station observations as well as live views of the skyline from people&#8217;s webcams and data from personal weather stations.</p>
<h3>Source #5: Weather.com XML Data Feed</h3>
<p>This one is not my favorite because it has the most limitations.  You get location specific information, current observations, and two-day forecasts, but there are several limitations:</p>
<ul>
<li>Must link back to weather.com</li>
<li>Can&#8217;t mix weather.com data with data from other sources</li>
<li>Don&#8217;t modify the data (makes sense to preserve their reputation)</li>
<li>Can&#8217;t redistribute the weather data</li>
<li>Must display a weather.com logo</li>
<li>Developed application must be free to use/access</li>
</ul>
<p>These are just a few of them, you will have to check out the data source to see your full list of requirements to use the data.  Point is, you will have to go through some legal mumbo-jumbo before you can begin coding your application.  The up-side with this source is that it is well documented.</p>
<p>View the data source here: <a href="http://www.weather.com/services/xmloap.html">http://www.weather.com/services/xmloap.html</a></p>
<p>So there you have it.  I have provided you with enough data to launch your own weather site. <img src='http://phpstarter.net/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' />   It&#8217;s up to you to parse and present the data.  The point of this post was to show you the vast amount of weather data that is widely available.  Watch our <a href="/feed/">RSS feed</a> for future posts, as I will be posting some implementation code on some of these data sources in the future.</p>
<p>If you have something you would like to see sample code for, or if you would like to see something explained in more detail, please let your voice be heard! (post a comment)</p>
<p>No related posts.</p>]]></content:encoded>
			<wfw:commentRss>http://phpstarter.net/2008/12/5-sources-of-free-weather-data-for-your-site/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>Several mod_rewrite Tricks for a Better Web Application</title>
		<link>http://phpstarter.net/2008/07/several-htaccess-mod_rewrite-tricks-to-better-web-application/</link>
		<comments>http://phpstarter.net/2008/07/several-htaccess-mod_rewrite-tricks-to-better-web-application/#comments</comments>
		<pubDate>Mon, 21 Jul 2008 12:42:51 +0000</pubDate>
		<dc:creator>Andrew</dc:creator>
				<category><![CDATA[Apache]]></category>
		<category><![CDATA[Advanced]]></category>

		<guid isPermaLink="false">http://wp.pr0gr4mm3r.com/?p=148</guid>
		<description><![CDATA[Apache&#8217;s .htaccess file options makes it easy to have clean URLs, smart redirects, and even control SSL connections.  In this post, I am going to give you several tips on how you make your web applications smarter.  Note that your server must support mod_rewrite in order to use these tips. Make Sure Everyone is on [...]
No related posts.]]></description>
			<content:encoded><![CDATA[<p>Apache&#8217;s .htaccess file options makes it easy to have clean URLs, smart redirects, and even control SSL connections.  In this post, I am going to give you several tips on how you make your web applications smarter.  Note that your server must support mod_rewrite in order to use these tips.</p>
<p><span id="more-7"></span></p>
<p><strong>Make Sure Everyone is on the Same Domain</strong></p>
<p>Even if you don&#8217;t have multiple domains pointing to your site, it&#8217;s possible that www.example.com <strong>and</strong> just example.com will work.  If that&#8217;s the case, Google and other search engines could be crawling your site twice and the website could take a ranking hit for duplicate content.  The following example shows how we can redirect visitors from two domains down to one.</p>
<blockquote><p>RewriteCond %{HTTP_HOST} ^www.example.net$ [NC,OR]<br />
RewriteCond %{HTTP_HOST} ^example.net$ [NC,OR]<br />
RewriteCond %{HTTP_HOST} ^www.example.com$ [NC]<br />
RewriteRule ^(.*)$ http://example.com/$1 [R=301,L]</p></blockquote>
<p><strong>Force SSL on Specific Site Directories</strong></p>
<p>I use SSL on a couple of my sites, but it doesn&#8217;t need to be enabled (or forced) on the entire site, so we use .htaccess mod_rewrite rules to enforce SSL where we need it.  The following peice of code will force SSL on all requests in the directory named &#8216;secure&#8217;.</p>
<blockquote><p>RewriteCond %{HTTPS} off<br />
RewriteRule ^secure/.*$ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]</p></blockquote>
<p>Both of these lines need to be copied if we need to enforce more than one directory, so if we need the directory &#8216;secure&#8217; and the the directory &#8216;users&#8217; secured, we would use this code.</p>
<blockquote><p>RewriteCond %{HTTPS} off<br />
RewriteRule ^secure/.*$ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]</p>
<p>RewriteCond %{HTTPS} off<br />
RewriteRule ^users/.*$ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]</p></blockquote>
<p>For the rest of the site, we don&#8217;t need SSL, so use something like this if we have the &#8216;secure&#8217; and &#8216;users&#8217; directory with SSL enabled.</p>
<blockquote><p>RewriteCond %{HTTPS} on<br />
RewriteCond %{REQUEST_URI} !secure/.*$<br />
RewriteCond %{REQUEST_URI} !users/.*$<br />
RewriteRule ^(.*)$ http://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]</p></blockquote>
<p><strong>Force SSL on a Domain or Sub-Domain</strong></p>
<p>Similar to the need above, we can force SSL on an entire domain if we need to.</p>
<blockquote><p>RewriteCond %{HTTPS} off<br />
RewriteCond %{HTTP_HOST} ^wells-it.com$ [NC]<br />
RewriteRule ^.*$ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]</p></blockquote>
<p><strong>Take a Website Offline</strong></p>
<p>If a website needs to go down for maintenance or whatever, that can be done with a simple few lines of code.  Just remember to change the page&#8217;s path in both lines where you see not-up.htm, or you could create a redirect loop.</p>
<blockquote><p>RewriteCond %{REMOTE_HOST} !^123\.214\.71\.126<br />
RewriteCond %{REQUEST_URI} !/static/not-up\.htm$<br />
RewriteRule .* /static/not-up.htm [R=302,L]</p></blockquote>
<p><strong>Remove &#8216;index.php&#8217; From the URL</strong></p>
<p>This is one that will come in handy if you use CodeIgniter or any other framework that sends all requests through index.php.  Basically, this code checks to see if the URL request exists as a static file or directory.  If does, it would be a picture, style sheet, javascript include, etc.  If it is not found, then it assumes that it must be for the framework to handle, and it sends it to the index.php file.</p>
<blockquote><p>RewriteCond %{REQUEST_FILENAME} !-f<br />
RewriteCond %{REQUEST_FILENAME} !-d<br />
RewriteRule ^(.*)$ /index.php/$1 [L]</p></blockquote>
<p><strong>Keep Visitors Off the Stage</strong></p>
<p>On my large sites, I have both a production copy and a stage copy.  The stage copy is where I do all my developing and testing.  When I am satisfied with the changes, I push the changes to my live site.  The problem is that I don&#8217;t want site visitors seeing my stage copy, so I use some mod_rewrite code to keep them off.  You need to know your IP address for this one, and if you have a dynamic IP, you will have to change the file every time you get a new one.</p>
<blockquote><p>RewriteCond %{REMOTE_HOST} !^56\.51\.98\.126<br />
RewriteCond %{HTTP_HOST} ^stage.wells-it.com$ [NC]<br />
RewriteRule ^(.*)$ http://wells-it.com/$1 [R=301,L]</p></blockquote>
<p>If you have multiple IP address that you want access to the stage, you can just add another condition.</p>
<blockquote><p>RewriteCond %{REMOTE_HOST} !^56\.51\.98\.126<br />
RewriteCond %{REMOTE_HOST} !^74\.65\.95\.128<br />
RewriteCond %{HTTP_HOST} ^stage.wells-it.com$ [NC]<br />
RewriteRule ^(.*)$ http://wells-it.com/$1 [R=301,L]</p></blockquote>
<p><strong>Turn .htaccess Into a Web Application Firewall</strong></p>
<p>I didn&#8217;t write this one, so I can&#8217;t take credit for it, but you can <a href="http://www.0x000000.com/?i=558" target="_blank">find the code here</a>.  It contains some very slick code to keep malicious page requests from getting through to your web applications.</p>
<p><strong>Final Notes</strong></p>
<p>Since some of these URL rewrites actually redirect the browser, it&#8217;s possible to get into a redirect loop where you will never satisfy the rules you have in place, and the browser will redirect forever&#8230;until it detects a loop and stops with an error.  If this happenes, try to see where the loop is or post a comment if you&#8217;re stuck.</p>
<p>No related posts.</p>]]></content:encoded>
			<wfw:commentRss>http://phpstarter.net/2008/07/several-htaccess-mod_rewrite-tricks-to-better-web-application/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>RSS &amp; Atom Feeds Made Easy for Developers</title>
		<link>http://phpstarter.net/2007/06/rss-atom-feeds-made-easy-for-developers/</link>
		<comments>http://phpstarter.net/2007/06/rss-atom-feeds-made-easy-for-developers/#comments</comments>
		<pubDate>Mon, 25 Jun 2007 15:23:00 +0000</pubDate>
		<dc:creator>Andrew</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[Advanced]]></category>

		<guid isPermaLink="false">http://wp.old.pr0gr4mm3r.com/2007/06/25/rss-atom-feeds-made-easy-for-developers/</guid>
		<description><![CDATA[In this entry you will find 2 feed templates ready to work on your TinyButStrong website. All you need to to is include these templates, set some variables, and merge the data block. The following templates are ready to go after including it in the TBS class. If you require more help or even implementation [...]
No related posts.]]></description>
			<content:encoded><![CDATA[<p>In this entry you will find 2 feed templates ready to work on your <a href="http://www.tinybutstrong.com/">TinyButStrong</a> website.  All you need to to is include these templates, set some variables, and merge the data block.</p>
<p><span id="more-4"></span></p>
<p>The following templates are ready to go after including it in the <span class="caps">TBS</span> class.  If you require more help or even implementation code, scroll down beyond the template code.</p>
<pre class="brush: xml">
&lt;?xml version=&quot;1.0&quot; encoding=&quot;[var.info.charset]&quot;?&gt;
&lt;rss version=&quot;2.0&quot;&gt;

   &lt;channel&gt;
      &lt;title&gt;[var.info.title]&lt;/title&gt;
      &lt;link&gt;[var.info.link]&lt;/link&gt;
      &lt;description&gt;[var.info.description]&lt;/description&gt;
      &lt;language&gt;[var.info.language]&lt;/language&gt;
      &lt;managingEditor&gt;[var.info.email]&lt;/managingEditor&gt;
                &lt;copyright&gt;Copyright [var.info.year]&lt;/copyright&gt;
      &lt;generator&gt;[var.info.generator]&lt;/generator&gt;
      &lt;pubDate&gt;[var.info.pub_date]&lt;/pubDate&gt;
      &lt;ttl&gt;60&lt;/ttl&gt;
      &lt;image&gt;
         &lt;link&gt;[var.info.link]&lt;/link&gt;
         &lt;title&gt;[var.info.img_title]&lt;/title&gt;
         &lt;url&gt;[var.info.img_url]&lt;/url&gt;
      &lt;/image&gt;

      [blkItems;block=begin]

      &lt;item&gt;
         &lt;title&gt;[blkItems.title]&lt;/title&gt;
         &lt;link&gt;[blkItems.link]&lt;/link&gt;
         &lt;comments&gt;[blkItems.comments]&lt;/comments&gt;
                        &lt;description&gt;&lt;![CDATA[ [blkItems.description] ]]&gt;&lt;/description&gt;
         &lt;guid isPermaLink=&quot;false&quot;&gt;[blkItems.guid]&lt;/guid&gt;
         &lt;category&gt;[blkItems.category]&lt;/category&gt;
         &lt;pubDate&gt;[blkItems.pub_date]&lt;/pubDate&gt;
      &lt;/item&gt;

      [blkItems;block=end]

   &lt;/channel&gt;
&lt;/rss&gt;
</pre>
<pre class="brush: xml">
&lt;?xml version=&quot;1.0&quot; encoding=&quot;[var.info.charset]&quot;?&gt;
&lt;feed xmlns=&quot;http://www.w3.org/2005/Atom&quot; xml:lang=&quot;[var.info.lang]&quot;&gt;
   &lt;title&gt;[var.info.title]&lt;/title&gt;
   &lt;subtitle&gt;[var.info.description]&lt;/subtitle&gt;
        &lt;link rel=&quot;alternate&quot; type=&quot;text/html&quot; href=&quot;[var.info.link]&quot;/&gt;
        &lt;link rel=&quot;self&quot; type=&quot;application/atom+xml&quot; href=&quot;[var.info.atom_link]&quot;/&gt;
   &lt;updated&gt;[var.info.updated]&lt;/updated&gt;
   &lt;author&gt;
   &lt;name&gt;[var.info.author_name]&lt;/name&gt;
   &lt;uri&gt;[var.info.link]&lt;/uri&gt;
   &lt;email&gt;[var.info.email]&lt;/email&gt;
   &lt;/author&gt;
   &lt;id&gt;[var.info.link]&lt;/id&gt;
   &lt;rights&gt;Copyright (c) [var.info.year], [var.info.author_name]&lt;/rights&gt;

   [blkItems;block=begin]

   &lt;entry&gt;
      &lt;title&gt;[blkItems.title]&lt;/title&gt;
      &lt;link rel=&quot;alternate&quot; type=&quot;text/html&quot; href=&quot;[blkItems.link]&quot; /&gt;
      &lt;updated&gt;[blkItems.updated]&lt;/updated&gt;
      &lt;published&gt;[blkItems.pub_date]&lt;/published&gt;
      &lt;id&gt;[blkItems.link]&lt;/id&gt;
      &lt;summary type=&quot;text&quot;&gt;[blkItems.summery]&lt;/summary&gt;
        &lt;content type=&quot;html&quot; xml:lang=&quot;[blkItems.lang]&quot; xml:base=&quot;[blkItems.link]&quot;&gt;&lt;![CDATA[
                [blkItems.content]
      ]]&gt;&lt;/content&gt;
      &lt;author&gt;
         &lt;name&gt;[blkItems.author]&lt;/name&gt;
      &lt;/author&gt;
   &lt;/entry&gt;

   [blkItems;block=end]

&lt;/feed&gt;
</pre>
<p>Please note that in my examples, I don’t include classes before I define them because I make use if PHP’s <a href="http://us2.php.net/manual/en/language.oop5.autoload.php">__autoload()</a> function. Since you probably don’t, insert the necessary include statement(s) before any object declaration statements. Most of the variables are self-explanatory in this file. Lines 12-23 define fields that are for the general feed, and data that does not pertain the the individual elements. Lines 30-48 take care of the feed items. In my example, I run a MySQL query, and feed that data into a complex array. You can populate that array any way you want as long as you keep those element names the same and keep that MergeBlock function alone online 48.</p>
<pre class="brush: php">
&lt;?php
   /* make sure that we are sending the correct header */
   header(&#039;Content-type: application/rss+xml&#039;);

   /* create template object */
   $tbs = new clsTinyButStrong();

   /* set main template */
   $tbs-&gt;LoadTemplate(&#039;rss_2_0.xml&#039;, false);

   /* define information fields */
   $info[&#039;charset&#039;] = &#039;utf-8&#039;;
   $info[&#039;title&#039;] = &#039;&#039;;
   $info[&#039;link&#039;] = &#039;&#039;;
   $info[&#039;description&#039;] = &#039;&#039;;
   $info[&#039;language&#039;] = &#039;en&#039;;
   $info[&#039;email&#039;] = &#039;&#039;;
   $info[&#039;year&#039;] = &#039;&#039;;
   $info[&#039;generator&#039;] = &#039;&#039;;
   $info[&#039;pub_date&#039;] = date(&#039;r&#039;);
   $info[&#039;img_link&#039;] = &#039;&#039;;
   $info[&#039;img_title&#039;] = &#039;&#039;;
   $info[&#039;img_url&#039;] = &#039;&#039;;

   /* for this section, you can include your entries through a query (if your column names
      match up with the fields), or a data array */

   /* I&#039;m going to use a data array from a MySQL table.  The following is an example on how to do the data block  */
   $query = &quot;SELECT * FROM Fields ORDER BY date DESC&quot;;
   $result = /* code to process your MySQL query */;

   for ($i = 0; $i &lt; mysql_num_rows($result); $i++)
   {
      $row = mysql_fetch_assoc($result);

      $blkItems[] = array(
                           &#039;title&#039; =&gt; &#039;&#039;,
                           &#039;link&#039; =&gt; &#039;&#039;,
                           &#039;comments&#039; =&gt; &#039;&#039;,
                           &#039;description&#039; =&gt; &#039;&#039;,
                           &#039;guid&#039; =&gt; &#039;&#039; . &#039;@&#039; . &#039;http://your.site.here&#039;,
                           &#039;category&#039; =&gt; &#039;&#039;,
                           &#039;pub_date&#039; =&gt; date(&#039;r&#039;, )
                        );
   }

   $tbs-&gt;MergeBlock(&#039;blkItems&#039;, $blkItems, true);

   /* show output, but don&#039;t stop the script */
   $tbs-&gt;Show(TBS_OUTPUT);
?&gt;
</pre>
<p>Please note that in my examples, I don’t include classes before I define them because I make use if PHP’s <a href="http://us2.php.net/manual/en/language.oop5.autoload.php">__autoload()</a> function. Since you probably don’t, insert the necessary include statement(s) before any object declaration statements. Most of the variables are self-explanatory in this file.<br />
Lines 12-23 define fields that are for the general feed. Lines 30-50 take care of the feed items. In my example, I run a MySQL query, and feed that data into a complex array. You can populate that array any way you want as long as you keep those element names the same and keep that MergeBlock function alone online 52.</p>
<pre class="brush: php">
&lt;?php
   /* make sure that we are sending the correct header */
   header(&#039;Content-type: application/atom+xml&#039;);

   /* create template object */
   $tbs = new clsTinyButStrong();

   /* set main template */
   $tbs-&gt;LoadTemplate(&#039;atom.xml&#039;, false);

   /* define information fields */
   $info[&#039;lang&#039;] = &#039;en-US&#039;;
   $info[&#039;charset&#039;] = &#039;utf-8&#039;;
   $info[&#039;title&#039;] = &#039;&#039;;
   $info[&#039;description&#039;] = &#039;&#039;;
   $info[&#039;author_name&#039;] = &#039;&#039;;
   $info[&#039;link&#039;] = &#039;&#039;;
   $info[&#039;email&#039;] = &#039;&#039;;
   $info[&#039;tag&#039;] = &#039;&#039;;
   $info[&#039;pub_date&#039;] = date(&#039;Y&#039;);
   $info[&#039;updated&#039;] = date(&#039;c&#039;);
   $info[&#039;year&#039;] = date(&#039;Y&#039;);
   $info[&#039;atom_link&#039;] = &#039;http://&#039; . $_SERVER[&#039;HTTP_HOST&#039;] . $_SERVER[&#039;REQUEST_URI&#039;];

   /* for this section, you can include your entries through a query (if your column names
      match up with the fields), or a data array */

   /* I&#039;m going to use a data array from a MySQL table.  The following is an example on how to do the data block  */
   $query = &quot;SELECT * FROM Fields ORDER BY date DESC&quot;;
   $result = /* code to process your MySQL query */;

   for ($i = 0; $i &lt; mysql_num_rows($result); $i++)
   {
      $row = mysql_fetch_assoc($result);

      $blkItems[] = array(
                           &#039;lang&#039; =&gt; &#039;en-US&#039;,
                           &#039;author&#039; =&gt; &#039;Andrew Wells&#039;,
                           &#039;title&#039; =&gt; htmlspecialchars($row[&#039;title&#039;]),
                           &#039;link&#039; =&gt; PATH_WEB_HOME . &#039;fields/&#039; . $row[&#039;slug&#039;] . &#039;/&#039;,
                           &#039;comments&#039; =&gt; PATH_WEB_HOME . &#039;fields/&#039; . $row[&#039;slug&#039;] . &#039;/&#039;,
                           &#039;summery&#039; =&gt; $row[&#039;intro&#039;],
                           &#039;content&#039; =&gt; $row[&#039;intro&#039;],
                           &#039;guid&#039; =&gt; $row[&#039;field_id&#039;] . &#039;@&#039; . PATH_WEB_HOME,
                           &#039;category&#039; =&gt; &#039;Fields&#039;,
                           &#039;updated&#039; =&gt; date(&#039;c&#039;, strtotime($row[&#039;updated&#039;])),
                           &#039;pub_date&#039; =&gt; date(&#039;c&#039;, strtotime($row[&#039;added&#039;]))
                        );
   }

   $tbs-&gt;MergeBlock(&#039;blkItems&#039;, $blkItems, true);

   /* show output, but don&#039;t stop the script */
   $tbs-&gt;Show(TBS_OUTPUT);;
?&gt;
</pre>
<p>No related posts.</p>]]></content:encoded>
			<wfw:commentRss>http://phpstarter.net/2007/06/rss-atom-feeds-made-easy-for-developers/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

