Programming Tutorial: Covering All Your Bases

Make sure you cover all your bases when reseting variables:

if ($flag == "yes") {
	$form->creationDate = null;
	$form->runCount = 0;
	$form->statusCode = 1;
} else {   
	$form->creationDate = null;
	$form->runCount = 0;
	$form->statusCode = 1;
}

Discovered by @zewillow

Programming Tutorial: Readability

Readability is always more important than simple one-liners:

if ($form->minute == 0) { 
	$minute = "00";
} else if ($form->minute < 10) {
	$minute = "0" . $form->minute;
} else {
	$minute = $form->minute;
}

Discovered by @zewillow.

Programming Tutorial: Variable Initialization

Always make sure your variables are properly initialized:

$list = $_SESSION['list'];

if ($list == null || $list == "") {
	$list = $_SESSION['list'];
}

Discovered by @zewillow

PHP Annoyances: header()

Earlier today, and yesterday in fact, I was successfully downloading generated PDF’s from a FLEX application from an Apache2 server via mod_php5. Tonight however, it kept failing with an IOErrorEvent #2038. There’s lots of fun references to this issue like this one. Sadly downloading via FileReference doesn’t trigger the HTTPStatusEvent event, so I can’t lie to FLEX.

I eventually stopped brute forcing an attempt and intelligently looked at Apache2′s logs and found this wonderful line:

10.10.220.130 – - [10/Feb/2009:20:24:16 -0500] “GET /vrm/Create_Report.php?download=vrm_report_17_10-10-220-130.pdf HTTP/1.1″ 1 68856 “-” “Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; …)”

Do you notice whats wrong? No? Look for the 1 after “HTTP/1.1″ and before the “68856″ – this is where the HTTP Status Code should be. Eh? What happened? Where’d my status go? The server-side looked like this:

if (file_exists($file)) {
	header('Content-Type', 'application/octet-stream', true);
	header('Content-Disposition', 'attachment/filename=' . $filename, true);
	header('Content-Length', filesize($file), true);
	header('Content-Description', 'File Transfer');
	readfile($file);
}

Any one notice the issue – header is being use inappropriately. The first argument should be the entire response header, the second argument is the option to override a matching header, and the third is the status code. “true” evaluates “1″ and thus the status of 1. You might ask as I am right now: Why is he divulging that he’s an idiot? Well folks it goes like this:

It’s 10:55PM and I’ve been programming since 8:30AM to meet a deadline. Things get missed and stupid mistakes happen. The above code worked on (IIS 6 + PHP5) but failed on (Apache2 + PHP5) – and this is one of the many reasons I hate PHP. The second argument should be a boolean and should SCREAM to the error log if you don’t match the type expected. Instead it says nothing, and leaves you to your madness.

Anyways, lets fix the code and go home!

	header('Content-Type: application/octet-stream', true, 200);
	header('Content-Disposition: attachment/filename=' . $filename, true);
	header('Content-Length: ' . filesize($file), true);
	header('Content-Description: File Transfer');

10.10.220.130 – - [10/Feb/2009:22:14:15 -0500] “GET /vrm/Create_Report.php?download=vrm_report_1_10-10-220-130.pdf HTTP/1.1″ 200 68856 “-” “Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; …)”

Gah ;-0

Monthly Calendars in PHP for Flex

At work I recently had a need to create a monthly calendar within Flex and populated with data from a web-service (created in PHP). I hate reinventing the wheel, but I also hate wasting time searching for a solution to a simple enough problem. So below you can find a quick answer to the problem as I couldn’t find one myself.

PHP: The following code creates a simple XML feed.


<?php
	/**
	 * Create a XML calendar for a given year &amp; month
	 *
	 * Noah Massey &amp; Matthew Metnetsky
	 */
	function calendar_xml($doc, $year, $month)
	{
		if (!$doc) throw new Exception('doc is bad');
		if (!$month) throw new Exception('month  is bad');
		if (!$year) throw new Exception('year is bad');
		
		$numDays = date('t', mktime(0, 0, 0, $month, 1, $year));
		
		$monthNode = $doc->appendChild($doc->createElement('month'));
		
		$day = 1 - date('w', mktime(0, 0, 0, $month, 1, $year));
		
		do {
			$weekNode = $monthNode->appendChild($doc->createElement('week'));
			
			for ($y=0;$y<7 and $day <= $numDays; $day++,$y++) {
				//$sunday = ($day == $y); 
				//$sunday = ($day == ($y + 6));
				$dayNode = $weekNode->appendChild($doc->createElement('day'));
				$numAttr = $doc->createAttribute('num');
				$numAttr->appendChild($doc->createTextNode($day));
				$dayNode->appendChild($numAttr);
				
				/*
				 * we could loop through database records here for holidays etc
				 * the _real_ version uses mktime(0, 0, 0, $month, $day, $year) 
				 * to check for a stamp within an array of holiday records
				 * and then tack on some more nodes to $dayNode
				 */
			}
		} while ($day <= $numDays);
	}
	
	$doc = new DOMDocument('1.0', 'UTF-8');
	$doc->formatOutput = true;
	
	$date = getdate();
	$year = (int) (array_key_exists('year', $_GET))? $_GET['year'] : $date['year'];
	$month = (int) (array_key_exists('month', $_GET))? $_GET['month'] : $date['mon'];
	
	calendar_xml($doc, $year, $month);	
	header('Content-Type: application/xml', true);
	echo($doc->saveXML());
?>

If you look at the rather large comment block above you can see where the value comes in. It’s pretty simple to use a unix timestamp to find records which you actually would want in the XML like holidays, events, etc.

The basic output looks like the following, and can be see in full at http://cowarthill.com/blog/wp-content/uploads/2009/01/calendar-xml.php

	<month>
		<week>
			<day num="{day of month}" />
		</week>
	</month>

Once you have all of this in place you need to pull it into Flex. I found using a DataGrid very easy once you have the XML from the web service. And to customize the display I created a custom itemRenderer for the DataGridColumns. Here’s a few snippets….

MXML: Lets define our DataGrid and our HTTPService which will retrieve our data.

<mx:DataGrid id="dg"
	sortableColumns="false"
	draggableColumns="false"
	selectable="false"
	showScrollTips="true"
	horizontalScrollPolicy="off"
	verticalScrollPolicy="off">
	<mx:columns>
		<mx:DataGridColumn headerText="Sunday" itemRenderer="calendar.MonthCell" />
		<mx:DataGridColumn headerText="Monday" itemRenderer="calendar.MonthCell" />
		<mx:DataGridColumn headerText="Tuesday" itemRenderer="calendar.MonthCell" />
		<mx:DataGridColumn headerText="Wednesday" itemRenderer="calendar.MonthCell" />
		<mx:DataGridColumn headerText="Thursday" itemRenderer="calendar.MonthCell"  />
		<mx:DataGridColumn headerText="Friday" itemRenderer="calendar.MonthCell" />
		<mx:DataGridColumn headerText="Saturday" itemRenderer="calendar.MonthCell" />
	</mx:columns>
</mx:DataGrid>

<mx:HTTPService id="calHS"
	url="http://localhost/project/calendar-xml.php"
	method="GET"
	result="handleCalXML(event);"
	fault="Alert.show('Failed to retrieve XML from server');"
	resultFormat="e4x"
	contentType="application/xml"/>

AS3: Now we need at least two functions: 1 to call the service and retrieve the XML; and a second take the response and assign it to the DataGrid.

/* month should be 1-based (January) */
public function retrieveCalendar():void
{
	calHS.url = 'http://localhost/project/calendar-xml.php?year=' + year + '&amp;month=' + month;
	calHS.send();
}

/* handle the results from from calendar-xml.php */
private function handleCalXML(event:ResultEvent):void
{
	var weeks:XMLList = event.result.week as XMLList;

	/* dg is defined above in the MXML */
	this.dg.dataProvider = dayInfo;
	this.dg.rowCount = weeks.length(); // grid show be no bigger than necessary
}

Now we’re going to create a reusable itemRenderer which is instantiated for each cell in the DataGrid. You can do this in MXML directly, but I like AS3. The most important function to focus on is the setter for data because it assigns the values based on the XML we received.

package calendar
{
	import mx.collections.XMLListCollection;
	import mx.controls.TextArea;
	import mx.controls.listClasses.BaseListData;
	import mx.controls.listClasses.IDropInListItemRenderer;
	import mx.controls.listClasses.IListItemRenderer;
	import mx.core.IDataRenderer;
	import mx.events.FlexEvent;

	public class MonthCell  extends TextArea 
							implements IDataRenderer, IDropInListItemRenderer, IListItemRenderer
	{
		private var _week:XMLListCollection = null;
		private var _day:XML = null;
		private var _listData:BaseListData;

		public function MonthCell()
		{
			super();

			this.editable = false;
			this.wordWrap = true;
			this.selectable = false;
		}

		[Bindable(FlexEvent.DATA_CHANGE)]
		public override function get listData():BaseListData
		{
			return this._listData;
		}

		public override function set listData(ldata:BaseListData):void
		{
			if (this._listData != ldata) {
			this._listData = ldata;
			this.dispatchEvent(new FlexEvent(FlexEvent.DATA_CHANGE));
			}
		}

		[Bindable(FlexEvent.DATA_CHANGE)]
		public override function get data():Object
		{
			return this._week;
		}

		public override function set data(value:Object):void
		{
			var xml:XML = value as XML;

			if (xml) {
				// convert XML to a list for extra methods
				this._week = new XMLListCollection(xml.children());

				// make sure we've got enough records in week
				// to be able to get the current day
				if (this._week.length > this.listData.columnIndex) {
					this._day = this._week.getItemAt(this.listData.columnIndex) as XML;
				} else {
					this._day = null; // clear in-case we're reused
				}
			}

			// do we have a valid day XML object
			if (this._week == null || this._day == null || int(this._day.@num) < 1) {
				this.htmlText = "";
			} else {
				/* lets display the day of the month in the cell */
				this.htmlText = "<p><b>" + this._day.@num + "</b></p>";
			}

			this.invalidateProperties();
			this.dispatchEvent(new FlexEvent(FlexEvent.DATA_CHANGE));
		}
	}
}