Snow days lead inevitably to template hacking.
Over the last couple of weeks, I’ve noticed that my “weatherbox” has started to show a broken image under certain conditions. I discovered that my XML stream is sending me an empty tag where it should be sending me the name of a graphic.
Under these brilliant conditions, my PHP was set to display “nothing.gif”, which, of course, led to the decidedly-unprofessional looking red X in place of the graphic. Today I made the PHP code just a tiny bit more robust by bracketing the <img> tag with an if condition. Read on to see the resulting code.
“I’m all about open source.”
Below is the code for the WeatherBox. The only parts I really contributed to it are the <? php ?> tags in the markup below. The code towards the top is the work of Michael Reilly (thanks again, Mike), which I found on php.net. The new if tags are boldfaced.
<?php
// Array to store current xml path
$ary_path = array();
// Array to store parsed data
$ary_parsed_file = array();
// Starting level – Set to 0 to display all levels. Set to 1 or higher
// to skip a level that is common to all the fields.
$int_starting_level = 1;
// what are we parsing?
$xml_file = ‘http://weather.interceptvector.com/weather.xml?&state=NJ&city=Wharton’;
// declare the character set – UTF-8 is the default
$type = ‘UTF-8′;
// create our parser
$xml_parser = xml_parser_create($type);
// set some parser options
xml_parser_set_option($xml_parser, XML_OPTION_CASE_FOLDING, false);
xml_parser_set_option($xml_parser, XML_OPTION_TARGET_ENCODING, ‘UTF-8′);
// this tells PHP what functions to call when it finds an element
// these funcitons also handle the element’s attributes
xml_set_element_handler($xml_parser, ‘startElement’,'endElement’);
// this tells PHP what function to use on the character data
xml_set_character_data_handler($xml_parser, ‘characterData’);
if (!($fp = fopen($xml_file, ‘r’))) {
die(“Could not open $xml_file for parsing!”);
}
// loop through the file and parse baby!
while ($data = fread($fp, 4096)) {
if (!($data = utf8_encode($data))) {
echo ‘ERROR’;
}
if (!xml_parse($xml_parser, $data, feof($fp))) {
die(sprintf( “XML error: %s at line %d”,
xml_error_string(xml_get_error_code($xml_parser)),
xml_get_current_line_number($xml_parser)));
}
}
xml_parser_free($xml_parser);
// Display the array
// print_r($ary_parsed_file);
// This function is called for every opening XML tag. We
// need to keep track of our path in the XML file, so we
// will use this function to add the tag name to an array
function startElement($parser, $name, $attrs=”){
// Make sure we can access the path array
global $ary_path;
// Push the tag into the array
array_push($ary_path, $name);
}
// This function is called for every closing XML tag. We
// need to keep track of our path in the XML file, so we
// will use this function to remove the last item of the array.
function endElement($parser, $name, $attrs=”){
// Make sure we can access the path array
global $ary_path;
// Push the tag into the array
array_pop($ary_path);
}
// This function is called for every data portion found between
// opening and closing tags. We will use it to insert values
// into the array.
function characterData($parser, $data){
// Make sure we can access the path and parsed file arrays
// and the starting level value
global $ary_parsed_file, $ary_path, $int_starting_level;
// Remove extra white space from the data (so we can tell if it’s empty)
$str_trimmed_data = trim($data);
// Since this function gets called whether there is text data or not,
// we need to prevent it from being called when there is no text data
// or it overwrites previous legitimate data.
if (!empty($str_trimmed_data)) {
// Build the array definition string
$str_array_define = ‘$ary_parsed_file’;
// Add a [''] and data for each level. (Starting level can be defined.)
for ($i = $int_starting_level; $i < count($ary_path); $i++) {
$str_array_define .= ‘['' . $ary_path[$i] . ”]’;
}
// Add the value portion of the statement
$str_array_define .= ” = ‘” . $str_trimmed_data . “‘;”;
// Evaluate the statement we just created
eval($str_array_define);
} // if
}
?>
<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN” “http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”>
<html xmlns=”http://www.w3.org/1999/xhtml”>
<head>
<meta http-equiv=”Content-Type” content=”text/html; charset=iso-8859-1″ />
<link rel=”stylesheet” href=”<$MTBlogURL$>styles-site.css” type=”text/css” />
<link rel=”alternate” type=”application/rss+xml” title=”RSS” href=”<$MTBlogURL$>index.rdf” />
<title>Ken & Sarah Walker | WeatherBox </title>
<script language=”javascript” type=”text/javascript” src=”<$MTBlogURL$>global.js”></script>
</head>
<body>
<? if ($ary_parsed_file['wid'] <> “”) { ?>
<img src=”http://weather.interceptvector.com/common/weather/icons/<?php echo $ary_parsed_file['wid']; ?>.gif” title=”<?php echo $ary_parsed_file['sky']; ?>” alt=”Weather Graphic” style=”float: right; margin: 20px;” />
<? } ?>
<div class=”sidetitle”>Weather</div>
<div class=”side-content”>
<br />
<?php echo $ary_parsed_file['city']; ?>, <?php echo $ary_parsed_file['state']; ?><br />
<?php echo $ary_parsed_file['sky']; ?><br />
<br />
Temp: <?php echo $ary_parsed_file['realtemp']; ?>°<?php echo substr($ary_parsed_file['tempScale'],0,1); ?><br />
Humidity: <?php echo $ary_parsed_file['humidity']; ?>%<br />
Barometer: <?php echo $ary_parsed_file['barometer']; ?>in<br />
Wind: <?php echo $ary_parsed_file['wind']['direction']; ?> at <?php echo $ary_parsed_file['wind']['strength']; ?>mph<br />
<?php echo $ary_parsed_file['date']; ?>
</div>
</body>
</html>
Hmm. Why not use the MT Plugin for weather, and the perl script from the MT-RSS plugin to cron a regular rebuild of the index pages? That way you don’t need to run realtime fetching/php, reduce your bandwidth needs, speed up page loads, and don’t get red x’s.
Well, I kind of like the whole real-time weather aspect of the PHP. Though, the stream is only refreshed every hour or so. Do you think it’s worth having a plugin rebuild the index page every hour? Aside from that, having a plugin rebuild the page at least everyday would be nice–then my calendar would be accurate!
If your stream only refreshes hourly, it’s definately worth the plugin rebuild. Just running the PHP takes server load, and can slow down your page. Dumping raw html is /much/ /much/ faster. In fact, another idea would be to replace the php for the rotating image with an img tag, where the src points at a php script. Then your index would load even faster, and the php src script could return a random image and its mime type, etc.
Lots of fun stuff.
Dude: Plugin URL, please…
ok.
http://mt-plugins.org/archives/entry/mtrss_feed.php
should start you on the right path. That is for parsing RSS feeds. It is compatible with MT-List if you want to parse lots of RSS feeds, otherwise it only uses one per tagset. Oh, and I believe that includes the perl script.
Now to get the weather plugin, go to http://mt-plugins.org/archives/entry/weather.php
You can then use the perl from MT-RSS_Feed to rebuild your indexes via cron, and the MT-Weather plugin will update your weather box in the process.
I don’t do the weather, but I use the RSS update on begroovy, when my server isn’t busy crashing.
Ryan, you rock. Thanks. I’ll have to spend another snow day working on this.
BTW, what did happen to the RyanAbramsWeb yesterday? I noticed the sites (begroovy, dcindustrial, etc.) were having some issues?
Well, the dedicated box seems to have a minor case of the hiccups. Hopefully they will stabilize, but I am thinking about cronning a regularly scheduled backup of the db’s… “just in case”