<?xml version="1.0" encoding="ISO-8859-1"?>
<rss version="2.0">
	<channel>
		<title>Blog</title>
		<link>http://www.dind.com/blog/workbench/</link>
		<description></description>
		<language>en-us</language>
		<pubDate>Fri, 18 May 2012 20:06:12 PDT</pubDate>
		<lastBuildDate>Fri, 18 May 2012 20:06:12 PDT</lastBuildDate>
		<generator>SiteCrafting.com GearBox 1.1 (beta)</generator>
		
		<item>
			<title>Using What We Build</title>
			<link>http://www.sitecrafting.com/blog/we-build-1/</link>
			<description>It's not often that I use the stuff we build. It's not that I don't support the businesses we do business with, it's more that I spend so much time online thinking of how we build things for our customers that I don't generally get a chance to actually use the websites we build in the way a customer would.I'm selling my house and as part of that process in Pierce County a  seller needs to complete a Report of System Status (RSS) and Operation  and Maintenance Report for a septic system. In order for your house to  close, you need to have a certified septic inspector pump your septic  system as well as fill out a form with the Tacoma Pierce County Health  Department. Once your system is marked as &quot;A-OK&quot; your sale is able to  go through. Prior to our intervention, a seller would need to  get an inspection, deliver the completed form to the Health Department  (don't forget the long line), wait for a response, then get a clean  bill of health to pass on to the new owner. This is much  easier now, thanks to SiteCrafting, although at the time it was built I  was not so sure how much easier it would make it. The answer is, a lot  easier. I looked up my as-built drawing here.I registered and added my septic company here.They posted my completed report here.I paid...and it was done...no lines, no problems...kinda makes me wish others built things to work so well.Other cool things we've built for TPCHD include:Online Permitting for Food Service OrganizationsFood Service Establishment ReportsA pretty cool Website Management Application</description>
			<pubDate>Thu, 12 Oct 2006 23:13:00 PDT</pubDate>
		</item>
			
		<item>
			<title>Smart Keyword Searching</title>
			<link>http://www.sitecrafting.com/blog/smart-keyword-searching-1/</link>
			<description>Earlier this week the brand new Pierce County Library website officially launched. It was a pretty complex build that took a lot of effort between ourselves and the library's staff to make sure that everything worked right for the site's users. One of the more important features on any website with a large amount of content is a solid search function, and this site was no different. With over 300 pages (and still growing), a user could easily have some difficulty tracking down the information that they are looking for. So what can you do to make finding information easier?Well, an intuitive structure for the pages on the site is a good start. Well-written copy and Meta information can also make it easier for a user or your search function to key in on what is important on a page. But even doing all this can still end up with more clicks and additional searching than are really needed.To try to alleviate some of this, we decided to use a slightly modified method for searching all the content on the site. In addition to searching page content and Meta information, the search function we developed now checks a table of keywords for matches. These keywords can be associated with a specific page on the site, which allows administrators a way to create some &quot;most likely&quot; matches.Administrator view of the smart keywords and the associated pagesPerforming a search for &quot;catalog&quot; will show a list of pages on the site that have something to do with the library catalog. What sets this search apart from any other search, say &quot;summer reading,&quot; is that there is a &quot;Quick Results&quot; section at the top of the list. If the search term that you've searched for is present in the keyword table, this section will highlight the page that administrators think is most relevant.Of course, this requires a bit of legwork up front to create a number of keyword and page associations, but it can greatly cut down on the frustration that your users can experience. The flexibility of the keyword mapping function also allows administrators to keep their search function current as certain keywords become more popular. When there is a large amount of data to search through, sometimes it can be tough to find the best info to show a user. By setting up these associations, you can help users find the information they're looking for a little quicker.</description>
			<pubDate>Tue, 17 Oct 2006 11:33:00 PDT</pubDate>
		</item>
			
		<item>
			<title>Printing without the Dialog</title>
			<link>http://www.sitecrafting.com/blog/printing-dialog/</link>
			<description>One day, one of our clients came to us with a very unusual request - they wanted to be able to print something directly from the browser, but without displaying the usual print dialog box. I don't have much time in the webdev business, but I've never heard of this kind of request, and neither had anyone else in the office.Initially, we didn't think that it was possible. The browser File &amp;gt; Print command uses a dialog box. So does the javascript print() function. The only reason that we knew this was possible was that the client's current system was already doing it. With a little research, we figured out how to print directly from the browser without displaying a dialog box.Tools you'll need:1 local Unix or Linux serverA pinch of Javascript1 PHP exec() callPHP to format your dataFirst: your server MUST be a local server, on the same network as the printer. This is because we'll be using the lp command to shoot off the print job.Second, to get this to work from the browser, you need a little javascript to call a file using XMLHttpRequest that will call the lp command and execute the print job.The actual command to start off the print job looks something like this: lpr -p printer file.txtThe final ingredient of this system is to use PHP to format the data you need to print, and then save it to a file. The lpr command then works on that file.In the end, we were able to get this whole thing working, but only because Dustin over at out co-location facility knew what needed to be done. Part of the command line printing involves manually creating a print spool - a task not for the faint of heart. The spool is defined in /etc/printcap, and looks something like this:printer:\&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; :ml=0:\&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; :mx=0:\&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; :sd=/var/spool/lpd/printer:\&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; :af=/var/spool/lpd/printer/printer.acct:\&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; :sh:\&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; :rm=XXX.XXX.XXX.XXX:\&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; :rp=text:\&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; :if=/etc/magicfilter/ibmpro-filter:\&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; :lpd_bounce=true:Replace the XXX string with your printer's IP address. Then, when you click on a print button, the javascript fires off the XMLHttpRequest, which calls the PHP script to create the print file, and executes the command to print that file. Or, you can call the script with a PHP function on a page load.Shout outs to Dustin and Andrzej, for making the printing work, and for getting the scripts to call the print action.</description>
			<pubDate>Mon, 09 Apr 2007 16:36:00 PDT</pubDate>
		</item>
			
		<item>
			<title>MS SQL Server 2005 text and ntext</title>
			<link>http://www.sitecrafting.com/blog/ms-sql-server-2005-ntext-1/</link>
			<description>How I discovered that text, ntext, and image data types have been deprecated and replaced by varchar(max), ntext(max), and varbinary(max).Recently, I needed to perform a query to update a number of links for the Pierce Count Library website. I thought I could use a simple REPLACE() string function, unfortunately, this turned out not to be the case. In order to update text and ntext datatypes using a query you're limited to a few functions, in my case I would be forced to use SUBSTRING() or UPDATETEXT().My original plan was to update all links in a field using a query like this:UPDATE web_pages SET web_pages.article1 = REPLACE( web_pages.article1, &quot;http://bad_link//&quot;, &quot;http://&quot;)Of course, when I tried this, I got an error that ntext datatypes are not allowed for the REPLACE function. So, I went online and begin searching for a function similar to REPLACE() that would work with ntext, that's when I found the msdn page for working with text, ntext, and image data types. I could have figured out a way to use SUBSTRING() or UPDATETEXT(), but  I determined that it would be much quicker to just look for the records that contained the bad links and update them manually.Later, I decided to do some research on the subject, because I just couldn't believe that there wasn't a way to use simple string functions with text and ntext. That's when I came across an informit article about Date, Math and Text Functions in SQL Server 2000 that explains how string functions don't work with text and ntext. Undaunted, I continued researching some more and that's when I found out that text, ntext, and image data types have been depracated for SQL 2005 and replaced by varchar(max), ntext(max), and varbinary(max). In fact, in future releases text, ntext, and image will no longer be supported. View the list at Deprecated Database Engine Features in SQL Server 2005.If I had only known. I would have designed all the fields to use nvarchar(max) instead of ntext. This would have solved a couple of problems for me. The first problem is that ntext data types can't be used in GROUP BY clauses and the second I would have been able to update the bad links a whole lot quicker by being able to use string functions. And if there was a need, I would have been able to use string functions in select statements to manipulate the text.</description>
			<pubDate>Fri, 27 Oct 2006 11:25:00 PDT</pubDate>
		</item>
			
		<item>
			<title>Why We Build Our Stuff from Scratch</title>
			<link>http://www.sitecrafting.com/blog/we-build-stuff-scratch/</link>
			<description>I had an interesting conversation with a potential client last Friday and it prompted me to think or at least explain our development rational.&amp;nbsp; He was looking to hire SiteCrafting to assemble a robust application for his client consisting of a design he came up with as well as some &quot;open-source&quot; technologies, and a few custom scripts generated by us. Long and the short of it...it seemed a disaster in the making. I mentioned to him that SiteCrafting builds our Web applications from the ground up, for each customer as an individual. We do not offer one-size fits all solutions, because just like anything one-size fits all it never fits anyone quite right.Using pre-built software in these instances can on the surface seem to be a cost-savings, but in fact it will most likely cost more money trying to piece technology together. A system built on workarounds from a variety of technologies will consistently need to be tweaked and modified. At SiteCrafting, we design our solutions to fit the organization perfectly, not just  most of the time, all of the time. Our process is simple, we learn about  a project, we design a solution, we build, test and deploy. The result is custom application built for you...not for the masses. Very few people utilize all of the features in an off the shelf solution like Microsoft Word or PowerPoint, but everyone pays for all of the features. Our model is different, establish a secure, scalable foundation, build what you need and will use, and pay for only that. When needs change in the future our flexible systems will be able to grow with you to adapt to your organizations needs.</description>
			<pubDate>Mon, 30 Oct 2006 13:04:00 PST</pubDate>
		</item>
			
		<item>
			<title>How To Full-Text Search</title>
			<link>http://www.sitecrafting.com/blog/to-full-search/</link>
			<description>Here's a quick how-to on implementing Full-Text Searching using Microsoft SQL Server 2005. Originally, I planned on just using LIKE statements in the WHERE clause of an sql query, however, this would not be possible since, as I posted earlier in MS SQL Server 2005 text and ntext, that string functions do not work on text and ntext data types.That's when I remembered Joe's blog entry about Cross Table Content Search, which he also mentioned in the office a couple of times before his entry. After that, I've been wanting to implement the Cross Table Content Search, and did not get the opportunity until developing the search page for the Pierce County Library. So, I delved into applying Cross Table Content Search and began researching ways to do that for Microsoft Server 2005. There are basically three parts, adding full-text to the database, creating a stored procedure, and creating a dataset to use that stored procedure.Add Full-Text IndexingThe very first step is ensuring that full-text indexing has been enabled for the database. The instructions I read online mentioned that this should be enabled when a new database is created, but I found this not to be the case for both our local testing server and the live server.These are steps to use when using SQL Server Management Studio for Microsoft Server 2005.Enable Full-text. The link is a how to, please note that the &quot;Use full-text indexing&quot; is a check-box near the top.Enable A Table for Full Text Indexing. Following these will cause a wizard to start. The first part is selecting which fields from the table should be indexed.The second part is creating a catalog or using an existing catalog. You can give a new catalog any name you wish and all the indexed fields will be added to this catalog.There's also options for creating a job that runs to update the catalog at a certain date and time, but this can be ignored if you chose to update the catalog automatically.After the wizard completes, right-click the table and then click Full-Text Indexing-&amp;gt;Start Full Population.The article Understanding SQL Server Full-text Indexing explains the wizard for SQL Server 200. Please look under the heading Enabling Full Text Indexing.Continue to enable each table that you wish to have Full-Text Indexing.Create a Stored ProcedureInitially, I intended to simply create a query in an asp.net 2.0 dataset and use a parameter for the search term. However, I quickly learned that queries and views can't use parameters when using the FREETEXT() and CONTAINS() predicates. Meaning Select * from kewl_gadgets Where CONTAINS(description, @search_terms) would fail. However, the parameter will work for a stored procedure.Here's a sample stored procedure:USE [kewlDatabase]GO/****** Object:&amp;nbsp; StoredProcedure [dbo].[spKewlSearch]&amp;nbsp;&amp;nbsp;&amp;nbsp; Script Date: 11/01/2006 17:18:14 ******/SET ANSI_NULLS ONGOSET QUOTED_IDENTIFIER ONGO-- =============================================-- Author:&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; Ken Foubert-- Create date: 11/1/2006-- Description:&amp;nbsp;&amp;nbsp;&amp;nbsp; Full Text Search on kewl gadgets content-- =============================================CREATE PROCEDURE [dbo].[spKewlSearch]( @searchWords AS nvarchar(255) ) AS&amp;nbsp;&amp;nbsp;  SELECT kewl_gadgets.name, kewl_gadgets.item_no, kewl_gadgets.costFROM kewl_gadets LEFT OUTER JOIN kewl_gadget_features ON kewl_gadgets.gadget_number = kewl_gadgent_features.gadget_numberWHERE CONTAINS( (kewl_gadgets.name, kewl_gadgets.description), @searchWords) OR CONTAINS(kewl_gadget_features.*, @searchWords) returnThere are two ways to tell which fields that CONTAINS() should look at.List field names from a table that should be searched on. This list needs to have parenthesis and the fields need to be full-text indexed, ie (kewl_gadgets.name, kewl_gadgets.description) Use table_name.* to tell CONTAINS()&amp;nbsp; to do a search on all the fields that have been full-text indexed.Create a CONTAINS() predicate for each table you wish to search on.NOTE: When passing the @searchTerm values, please make sure that the words have quotes around them when using the CONTAINS(). @searchTerm = &quot;kewl radios&quot;. You can parse out the search term to be @searchTerm = &quot;kewl&quot; and &quot;radios&quot; or @searchTerm = &quot;kewl&quot; near &quot;radios&quot;, etc.This stored procedure is good for simple searches. You have an option of also using FREETEXT() which looks for words that are similar to the search terms by using a thesaurus.To learn more click CONTAINS or FREETEXT.In addition, with Full-text indexing you can get results that return a relevancy number by using CONTAINSTABLE or FREETEXTTABLE. For additional information take a look at Full-Text Search Developer InfoCenter.Create DatasetThe final step is to create a dataset or data adapter to use the stored procedure. I will not go into detail on how to do this. I leave that up to you.</description>
			<pubDate>Fri, 03 Nov 2006 10:51:00 PST</pubDate>
		</item>
			
		<item>
			<title>Emulating Form POST in PHP</title>
			<link>http://www.sitecrafting.com/blog/emulating-form-post-in-php-1/</link>
			<description>
Recently, I had a need to POST a form, using PHP script, to a remote url.&amp;nbsp; As I started to research a solution, I soon realized there were very few limited options.

Here's the situation, a customer has added a number of items to his shopping cart and ready to submit the final order. When the submit button is pressed, the PHP script processees the order locally, and then the next step is to POST the shopping cart to a remote url, which I have no control over. This system is very similar to integrating a website with PayPal.Here are my choicesJavascriptThe shopping cart is processed locally. Afterwards the php script creates an html page with a hidden POST form with the needed values. We add Javascript to submit the form when the page loads.CommentUnfortunately, this was not an option, since there's a strong possibility that some customers will have javascript disabled.cURL and Header(&quot;Location:&amp;nbsp; http://www.theremoveurl.com&quot;)In this example, cURL (or fsockopen) is used to post the variables to the remote. After the cURL operation is complete use Header(&quot;Location: url&quot;) to redirect to the website.It would also be possible to get the response, which should be a html and echo() or print() it to the customer's browser.CommentThis was not an option since the remote creates a session and redirects to an unknown URL after the form has been submitted. There's no way to transfer the session information (well maybe) to the customer's browser and then redirect the browser to the final url by using the header information from the cURL response.  Intermediate PageAfter the customer presses the submit button, the PHP script processes the order locally. Next, an intermediate page is used that says something like, &quot;Thank you, your order has been submitted. Please press the OK button to continue on to your next companies web site. If you don't, then your company will not know about the order you just placed and bad things might happen.&quot;CommentThis was the best solution to the problem at hand.</description>
			<pubDate>Fri, 19 Jan 2007 16:52:00 PST</pubDate>
		</item>
			
		<item>
			<title>Optimizing GROUP BY  with Multiple LEFT JOINS</title>
			<link>http://www.sitecrafting.com/blog/optimizing-group-by-multiple-left/</link>
			<description>At SiteCrafting, I enjoy working with a large number of different projects, each with their own requirements, technology, and problems to be solved, unfortunately, I sometimes forget about past solutions, until after I have finished writing a piece of code. Such is the case with a query that was eating up some serious processing time.The problem was with a GROUP BY query with LEFT JOINs to several other tables and summing up totals from those joined tables. This query was taking about 4 minutes 45 seconds to run, and worse yet, it was affecting searches which had nothing to do with that query, and probably also eating up precious memory and cpu resources.In order to optimze the table I used the EXPLAIN
statement to figure out where the bottle necks were and then
restructured the primary keys and indexing for the tables I was trying
to left join to. This reduced the time of the query to about 35
seconds, this was still too slow and was still eating up too many
resources. I decided to put the problem to the side and
continued working another project. That's when I remembered using
temporary tables to solve a similar problem for a past project. The
temporary tables solution reduced the query time to about .5 seconds.Below is an example of the temporary table solution that I implemented for MySQL 4.1 and PHP 4.0. PLEASE NOTE: The database user must have the CREATE TEMPORARY TABLE privelege to the database.Let's start with three sample tables that a hotel uses.CREATE TABLE `rooms` (&amp;nbsp; `room_id` int(11) NOT NULL auto_increment,&amp;nbsp; `floor_number` int(10) NOT NULL, &amp;nbsp; `room_number` varchar(10) default NULL,&amp;nbsp; `size` varchar(10) default NULL,&amp;nbsp; `beds` int(11) default NULL,&amp;nbsp; `towel_allotment` int(11) default NULL,&amp;nbsp; PRIMARY KEY&amp;nbsp; (`room_id`)) TYPE=MyISAMCREATE TABLE `room_services` (&amp;nbsp; `service_id` int(11) NOT NULL auto_increment,&amp;nbsp; `room_id` int(11) default NULL,&amp;nbsp; `date_serviced` datetime default NULL,&amp;nbsp; `serviced_by` varchar(50) default NULL,&amp;nbsp; `order_amount` float default NULL,&amp;nbsp; PRIMARY KEY&amp;nbsp; (`service_id`),&amp;nbsp; KEY `room_id` (`room_id`)) TYPE=MyISAMCREATE TABLE `room_cleanings` (&amp;nbsp; `cleaning_id` int(11) NOT NULL auto_increment,&amp;nbsp; `room_id` int(11) default NULL,&amp;nbsp; `date_cleaned` datetime default NULL,&amp;nbsp; `cleaned_by` varchar(50) default NULL,&amp;nbsp; PRIMARY KEY&amp;nbsp; (`cleaning_id`),&amp;nbsp; KEY `room_id` (`room_id`)) TYPE=MyISAMFor some bizarre reason, the management wants a single report that lists the&amp;nbsp; number of times a room received room service and cleaning, even for the new rooms that were added, which have not received any services or cleaning. We start with this query.SELECT rooms.room_id, rooms.floor_number, rooms.room_number,COUNT( DISTINCT room_services.service_id) AS vTotalServices, COUNT( DISTINCT room_cleanings.cleaning_id) AS vTotalCleaningsFROM rooms LEFT JOIN room_services ON room_services.room_id = rooms.room_idLEFT JOIN room_cleanings ON room_cleanings.room_id = rooms.room_idGROUP BY rooms.room_id ORDER BY rooms.floor_number, rooms.room_number This looks like a good query. All three tables use the room_id as an index, which is numeric, another plus that queries like. However, this query is too slow, but why should that be.In order to figure that out, we need to take a look at the total number of records that is being generated by this query. Let's say that there are 100 records for the rooms table, 40,000 for room_services, and 10,000 for room_cleanings. When using left joins, to get the total number of records, where there is a one-to-many relationship, we multiple all the records together.100 rooms * 40,000 services * 10,000 cleanings = 3,990,000 totalThe sample query has almost 4 million records to count up.This is where a single temporary table can be of help. The room_services table has the most records, a temporary table can be used to store the total number of services for that room. PLEASE NOTE: In PHP, a temporary table will exist as long as a connection remains open.Here's a step-by-step example in PHP and MySQLSTEP 1 - create the temporary table// $connection is a mysql_connection$sql = &amp;lt;&amp;lt;&amp;lt;EODCREATE TEMPORARY TABLE `tmp_service_totals` (&amp;nbsp; `room_id` int(11) NOT NULL default '0',&amp;nbsp; `total_services` int(11) default NULL,&amp;nbsp; PRIMARY KEY&amp;nbsp; (`room_id`)) TYPE=MyISAMEOD;$res = mysql_query($sql, $connection);STEP 2 - Populate the temporary table// the temporary table stores the total number of services for each room$sql = &amp;lt;&amp;lt;&amp;lt;EODINSERT INTO `tmp_service_totals` (room_id, total_services )SELECT room_services.room_id, COUNT(DISTINCT room_services.service_id )FROM room_services GROUP BY rooms_services.room_idEOD;$res = mysql_query($sql, $connection);STEP 3 - Update our original query using the temporary table// query to get the totals for each room$sql = &amp;lt;&amp;lt;&amp;lt;EODSELECT rooms.room_id, rooms.floor_number, rooms.room_number,tmp_service_totals.total_services AS vTotalServices, COUNT( DISTINCT room_cleanings.cleaning_id) AS vTotalCleaningsFROM rooms LEFT JOIN tmp_service_totals ON tmp_service_totals.room_id = rooms.room_idLEFT JOIN room_cleanings ON room_cleanings.room_id = rooms.room_idGROUP BY rooms.room_id ORDER BY rooms.floor_number, rooms.room_number EOD;$res = mysql_query($sql, $connection);STEP 4 - Store the records into an arraywhile( $record = mysql_fetch_assoc($res) ){&amp;nbsp;&amp;nbsp;&amp;nbsp; $records[] = $record;}    STEP 5 - Destroy the temporary table$sql = &amp;lt;&amp;lt;&amp;lt;EODDROP TABLE tmp_service_totalsEOD;SummaryLet's take another look at the query in step 3. Since the left join between rooms and  tmp_service_totals is one-to-one, we will not need to factor that in to determine the total number of records that was generated. 100 rooms * 10,000 cleaning = 100,000 total recordsSo, we went from having four million records TO one hundred thousand records.For my real world query, after optimizing tables structures and indexes the time was averaging 34 seconds. After implementing the temporary table, the final average time was about .5 seconds, including the creation of the temporary table and inserting data into that table.  </description>
			<pubDate>Tue, 20 Feb 2007 14:53:00 PST</pubDate>
		</item>
			
		<item>
			<title>PHP cURL and SSL Connection Timeout</title>
			<link>http://www.sitecrafting.com/blog/php-curl-ssl-connection-timeout/</link>
			<description>I've been working on a web site that uses cURL for request/response transactions and I was encountering a number of problems with empty responses from the server. This caused some frustrations, since these empty responses caused entire sections of a web page to be empty, especially on searches. Well, I couldn't have that, since people using the website would be confused on why a search for an item only worked occasionally. Trust in the web application would drop dramatically.My first instinct was to blame the server for returning empty responses, but realized that I shouldn't jump to conclusions about other people's servers. So, I launched myself into debugging the request/response transaction.Let's take a look at some PHP code for performing a cURL transaction. The most important thing to remember is that I can't stop the processing if an error occurs. So, if there is an error, the function returns an empty string.// keep looping until we get a response$count = 0;// try to get a good response by looping 10 timeswhile( $response == &quot;&quot; ){&amp;nbsp;&amp;nbsp; &amp;nbsp;$count++;&amp;nbsp;&amp;nbsp; &amp;nbsp;$response = curl_request( &quot;https://php.net&quot;, &quot;search=curl&quot; );&amp;nbsp;&amp;nbsp; &amp;nbsp;// if we looped ten times and still no response, lets quit to avoid never ending loop&amp;nbsp;&amp;nbsp; &amp;nbsp;if( $count == 10 )&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;break;}&amp;nbsp;&amp;nbsp; &amp;nbsp;//end while loopfunction curl_request($server_url, $values){&amp;nbsp;&amp;nbsp; &amp;nbsp;// this array stores all the information for a cURL transaction&amp;nbsp;&amp;nbsp; &amp;nbsp;$arrCurlInfo = array();&amp;nbsp;&amp;nbsp; &amp;nbsp;$arrCurlOptions = array();&amp;nbsp;&amp;nbsp; &amp;nbsp;$arrCurlOptions[CURLOPT_HEADER] = 1;&amp;nbsp;&amp;nbsp; &amp;nbsp;$arrCurlOptions[] = &quot;&quot;;&amp;nbsp;&amp;nbsp; &amp;nbsp;// set to 1 to include header info from response&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;$arrCurlOptions[CURLOPT_HEADER] = 1;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; //Return the output from the cURL session rather than displaying in the browser.&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; //If already set, don't overwrite&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; $arrCurlOptions[CURLOPT_RETURNTRANSFER] = 1;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; // if page redirects, curl will follow&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; $this-&amp;gt;setCurlOption(CURLOPT_FOLLOWLOCATION, 1, false); // &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; //If already set, don't overwrite&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; $arrCurlOptions[ CURLOPT_USERAGENT] = $_SERVER['HTTP_USER_AGENT'];&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; / attempt the request/response&amp;nbsp;&amp;nbsp;&amp;nbsp; try&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; {&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; $fp = curl_init();&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; curl_setopt_array($fp, $arrCurlOptions);&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; $response = curl_exec($fp);&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; $arrCurlInfo = curl_getinfo($fp);&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; }&amp;nbsp;&amp;nbsp;&amp;nbsp; //end try&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; // catch any errors that occur and return an empty response&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; catch( Exception $e)&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; {&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; $strResponse = &quot;&quot;;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; $strErrorCode = $e-&amp;gt;getCode();&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; $strErrorMessage = $e-&amp;gt;getMessage();&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; log_curl_error($arrCurlInfo, $strErrorCode, $strErrorMessage);&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; }&amp;nbsp;&amp;nbsp;&amp;nbsp; //end catch&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; curl_close($fp);&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; $fp = null;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; return $response;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&amp;nbsp;&amp;nbsp;&amp;nbsp; //end functionThe log_curl_error() is a function that simply stores the cUrl information and the error code and message to a database table. Now, let's pretend that I run this script a large number of times. Once in a while, no response is returned. I take a look at the log table and notice that I get the same error message of &quot;SSL connection timeout&quot; and they are always in groups of ten.Hmm, that must mean that even though we're re-initializing the cURL each time we loop through, it must be using the same connection each time. So, once the first connection is bad, each call to curl_request() uses the same connection. After some research, I found out that the cURL connection is cached.I'm thinking that for some reason, the web site and the server have trouble connecting using &quot;HTTPS,&quot; but since the connection is cached, we keep getting an empty response, no matter how many times we loop. During the afore mentioned research, I also discovered a cURL option,CURLOPT_FRESH_CONNECT, for clearing the cache. Additional cURL options.Using this new insight, I added one more cURL option to the curl_request() function.$arrCurlOptions[CURLOPT_FRESH_CONNECT] = 1;By adding this option, the connection is cleared each time we run the loop. For example, the first time through the loop, the SSL connection between the web site and server is bad, and we get a &quot;SSL connection timeout&quot; error. The second time through, the connection is refreshed, this time the SSL connection between the web site and server is good. The function is now able to return a response from the server. </description>
			<pubDate>Mon, 02 Apr 2007 14:36:00 PDT</pubDate>
		</item>
			
		<item>
			<title>How To Run PHP Code In The Background</title>
			<link>http://www.sitecrafting.com/blog/to-run-php-code-in/</link>
			<description>If you have php code that would take longer than 5 minutes to run, what would you do? You could update the configuration file for apache, or whatever web server you're running, to increase the amount of time a script page is allowed to run&amp;nbsp; and then use php's ini_set(&quot;max_execution_time&quot;, seconds) to increase the amount of time php will allow a script to run.Or you could use a session variable to mark the last position of the script and then use meta-refresh to start the script at the last position.Or you could try an asynchronous call by using php's exec command.Asynchronous ExampleHere's an oversimplified example. Let's say that you've been charged with resizing all the images in the Products folder. You decide to write some fancy php script to iterate through all the sub-folders and resize each image. Everything works beautifully on your test server with 50 images and 10 sub-directories. You upload your script to the live server, with a couple thousand images, and attempt to run it. After about 5 minutes the page times out and only about 200 images were resized.Now what? One possible solution is to create two pages, one that does all the image resizing, image_resize.php, and another that calls that page, resize_exec.php using the exec command.resize_exec.php&amp;lt;%$command = &quot;/usr/bin/php4 -f /var/www/myweb/image_resize.php&quot;;exec( &quot;$command &amp;gt; /dev/null &amp;amp;&quot;, $arrOutput );%&amp;gt;$command is a linux/debian command for running a php page through a command line. Normally, exec() will wait for the script to finish running and will return any output to the array $arrOutput, like error messages. However, by adding &quot; &amp;gt; /dev/null &quot;, the exec() will not wait for the script to finish, allowing the php script to run in the background.Your image_resize.php script is now able to run completely to the end.What to watch forOn the page that does the actual work, be sure to use ini_set(&quot;max_execution_time&quot;, seconds) to increase the amount of time php will allow your page to run.Be sure to use absolute paths for the command line and the&amp;nbsp; working page.Make sure that permissions are set up correctly to allow the php command line to run.The command line and &quot; &amp;gt; /dev/nul/&quot; works with linux/debian. This may be different, depending on your system.This example will not work with windows, although it may be possible to do same thing using a similar method.Additional FeaturesWhat if you wanted to keep track of the image resize process. One idea is to have image_resize.php update a text file with the percent done as it's running. Then on the resize_exec.php page, have a little bit of ajax that reads that file every few seconds and displays it on the page.You could also have image_resize.php send out an e-mail informing you that it has finished.</description>
			<pubDate>Fri, 13 Apr 2007 14:19:00 PDT</pubDate>
		</item>
			
		<item>
			<title>PHP PEAR and include_path</title>
			<link>http://www.sitecrafting.com/blog/php-pear-includepath/</link>
			<description>        When attempting to use the SMTP library from PEAR for a project, I was having problems with including the file. I kept getting a file stream error for this code:    	require 'Net/SMTP.php';I figured the problem had to be related to the include_path setting in the configuration file.When PEAR is installed, the include path in the PHP ini file is updated with the path to PEAR. Also, there's another path to the php folder, which is used by PEAR. So, if it's missing, PEAR includes will not work.      	Here's an example of the master include path:path = .:/usr/share/php:/usr/share/pear    	However, in the configuration,  the ini_set( &quot;include_path&quot;, &quot;../include/&quot;); overrides that setting,  which prevents the require() function from working.&amp;nbsp; At first, I&amp;nbsp; figured I only needed to add the path for pear, so I tried setting the include_path this way.ini_set(&quot;include_path&quot;,&quot;/usr/share/pear:../include/&quot; );But that include failed also. That's when I figured out that PEAR also uses the &quot;/usr/share/php&quot; path and possibly the &quot;.&quot; path, which is the path for the&amp;nbsp; current directory. Being the lazy programmer that I am, I didn't want to have to worry about updating these paths for different environment and settings, so I used this code snippet instead.$strMasterInclude = get_include_path();ini_set( 'include_path', &quot;{$strMasterInclude}:../include/&quot;);Now, the paths required for PEAR will always be included, no matter the environment.NOTE:In this example, the file paths are separated by a colon &quot;:&quot;, but the paths could also be separated by a semi-colon &quot;;&quot;, depending on your setup.</description>
			<pubDate>Wed, 18 Apr 2007 13:08:00 PDT</pubDate>
		</item>
			
		<item>
			<title>ASP.NET 2.0 Tips - Shared Functions</title>
			<link>http://www.sitecrafting.com/blog/aspnet-20-tips-shared-functions/</link>
			<description>When developing a recent project for ASP.NET, there was a need to migrate a large number of generic functions that were created in PHP into the .net web project. For .net 1.0, you were able to add a code page that could be used for creating a bunch of functions. However, in .net 2.0, you still had the ability to add a code page, but it had to be a class. This meant placing all of our migrated functions as methods inside a class. To use a generic function, such as generateNewPassword(), you would have to first create a new object for the class and then call the method.Example:Dim objGenericFunctions as new GenericFunctions()blnPasswordSent = generateNewPassword(firstName, lastName, email)There are a couple of problems with this technique. First, since .net 2.0 encourages object-oriented programming, you could either create objGenericFunctions() once and pass the object as a parameter to other classes. But this requires extra code management, especially when there are classes that call other classes that might need to use some of those Generic Functions from objGenericFunctions. Or you could create a new objGenericfunctions whenever you need it, but this means creating a number of objects for the GenericFunctions class, which seems to be a waste of system resources. In developing classes for PHP 5.0, you could declare a class function as static. Which allowed you to use that method without having to create a new object. After some googling, I learned that VB.net also has a similar declaration, but they call it shared. So, by declaring a class function as shared, there's no need to create a new object each time.Example:blnPasswordSent = GenericFunctions.generateNewPassword(firstName, lastName, email)Here's a sample class with a shared function:Imports Microsoft.VisualBasicImports System.WebPublic Class GenericFunctions&amp;nbsp;&amp;nbsp; Public Shared Function isDateInRange(ByVal dateStart As DateTime, ByVal dateEnd As DateTime, ByVal dateCompare As DateTime) As Boolean&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Dim blnInRange As Boolean = False&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Dim intStartDiff As Integer = 0&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Dim intEndDiff As Integer = 0&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; intStartDiff = Date.Compare(dateStart, dateCompare)&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; intEndDiff = Date.Compare(dateCompare, dateEnd)&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; If intStartDiff &amp;lt;= 0 And intEndDiff &amp;lt;= 0 Then&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; blnInRange = True&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; End If&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Return blnInRange&amp;nbsp;&amp;nbsp;&amp;nbsp; End FunctionEnd ClassWe can take this one step for further by adding a namespace declaration to the class. By using a namespace, we can now add Imports GenericFunctions to the top of a code page and access those methods without having to type GenericFunctions first.Sample of Namespace declaration:Imports Microsoft.VisualBasicImports System.WebNamespace GenericFunctions&amp;nbsp;&amp;nbsp;&amp;nbsp; Public Class GenericFunctions&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ' Place all the functions here...&amp;nbsp;&amp;nbsp;&amp;nbsp; End ClassEnd NamespaceExample of using the Imports statement:Default.aspx.vb - Code BehindImports GenericFunctionsPartial Class Default&amp;nbsp;&amp;nbsp;&amp;nbsp; Inherits System.Web.UI.Page&amp;nbsp;&amp;nbsp;&amp;nbsp; Protected Sub Page_Load(ByVal sender as Object, ByVal e As System.EventArgs) Handles Me.Load&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 'No longer need to start with GenericFunctions&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; blnPasswordSent =generateNewPassword(firstName, lastName, email)&amp;nbsp;&amp;nbsp;&amp;nbsp; End SubEnd Class</description>
			<pubDate>Mon, 04 Jun 2007 11:31:00 PDT</pubDate>
		</item>
			
		<item>
			<title>ASP.net 2.0 Gridview vs. Custom Coding</title>
			<link>http://www.sitecrafting.com/blog/aspnet-20-gridview-vs-custom/</link>
			<description>I recently decided to try the Gridview control in asp.net 2.0. I read some documentation and decided that it would be very easy to implement. I started by following a step-by-step tutorial on creating a table using the Gridview control, which uses the SqlDataSource control. This step-by-step tutorial included details on how to add paging, column sorting, updating a record, and deleting a record. The tutorial can be found here. I was actually excited when I got this to work and how easy it was to do. It would only take a few minutes to create a simple, paged, sortable table where a user can delete and update records right on the page. Please note that simple is the operative word here.The next step was to start modifying the table's appearance to be similar to our standard table layout and colors and using images for the edit, delete, save, and cancel buttons. I first decided to tackle adding images to the&amp;nbsp; buttons. The default grid has a simple hyperlink button titled &quot;Edit&quot; and &quot;Delete&quot; and if you selected to &quot;Edit&quot; a record, those links were replaced by &quot;Update&quot; and &quot;Cancel.&quot; As I began looking into ways to change this, I soon learned it's not that simple. In order to use my images, I needed to add a new column to the Grid for a command field. This command field allowed me to select images for &quot;Edit&quot;, &quot;Delete&quot;, &quot;Update&quot;, and &quot;Cancel&quot; but it took me some time reading online tutorials and searching the Microsoft Visual Studio 2005 Documentation in order to figure out how to do it.I then updated the look and feel of the gridview to match our standard layout. That turned out to be pretty simple, since all I did was select a standard theme that's available to the gridview that was fairly close. I also wanted to hide the column that displayed the id number for each record, since a user has no need for it. Unfortunately, the Gridview needs that column in order to perform the delete and edit operations. I thought about updating the styling of cells under the id column to have a width of 0, but I didn't to want spend any more time on it. Next, I like how you could press the &quot;Edit&quot; icon or text and then that row would be converted to a simple form. So, I wanted to do the same thing for adding a new record. Ooops, that's not so easy either. It turns out that the Gridview does not have anything built in for adding a new record. After googling, there turned out to be a number of methods to simulate that behavior. Since I was in a hurry, I decided to go with the easiest one to implement, which is basically displaying a simple add form instead of the table. To see how this was done, please refer to this article. Unfortunately, this was not what I wanted.Now, the next thing I wanted to do was add a simple javascript to confirm that the user wants to delete the record. How hard could that be? Well, it's not exactly hard, I mean none of it is hard, just time consuming. In order to add javascript, I would need to convert the Gridview columns into templates in order to add the OnClientClick attribute to the delete icon button. Unfortunately, by converting to templates, that meant turning off the EnableSortingAndPagingCallbacks for the Gridview. Which meant I would have to do some more research on how to manually sort and page the Gridview. I decided that if a person clicked the delete, then the item will get deleted, no questions asked.What I learned was the more I features I wanted to add to the Gridview, the more research and coding that was required. In comparison, I decided to programatically write a table similar to Gridivew. I was able to add code for paging, column sorting, button to edit and delete a row, and a row for adding a new record. I was also able to add javascript to confirm a deletion. The edit button worked exactly the same as the Gridview, if you pressed edit, then that row would be converted to a simple form, with icon buttons for saving and cancelling. In addition, I was able to move the paging controls anywhere I wanted and to style it in any way. I also added a previous and next buttons, along with listing all the page numbers. At first glance, I couldn't find a way to this in the Gridview control.When comparing these two methods, Gridview vs Custom Coding, I spent twice as long creating the Gridview than I did using the custom coding method. However, this has more to do with the steep learning curve for the Gridview, especially when you need certain features, so I figure coding time could eventually be about the same, and would actually be a lot faster if the table is simple. The one advantage of Custom Coding, is that you can tailor your table to look and behave&amp;nbsp; exactly as you wish, especially if you wish to incorporate a lot of javascript. Hope this helps in determining when to use the Gridview or any other control.</description>
			<pubDate>Mon, 23 Jul 2007 17:29:00 PDT</pubDate>
		</item>
			
		<item>
			<title>PHP Code in The Background Part 2</title>
			<link>http://www.sitecrafting.com/blog/php-code-in-background-part/</link>
			<description>The blog entry How To Run PHP Code In The Background discussed how to run a php file through the command line using php's exec() command.&amp;nbsp; Recently, I needed to pass a querystring to the php file, but this is not possible through the command line. If you try to pass a querystring, the exec() command will fail.Fortunately, after some googling, I came across a forum post that mentioned how the global array $_SERVER[&quot;argv&quot;] can be used to get arguments being passed through php. I then followed the link on that page to Chapter 43. Using PHP from the command line on&amp;nbsp; the php.net website. Under the user contributed notes, I found a nice little function for getting the arguments and placing them into an array, similar to $_GET, $_POST, and $_REQUEST.Let's take the example from the original How To Run PHP Code In The Background blog entry.resize_exec.php&amp;lt;%$command = &quot;/usr/bin/php4 -f /var/www/myweb/image_resize.php&quot;;exec( &quot;$command &amp;gt; /dev/null &amp;amp;&quot;, $arrOutput );%&amp;gt;Now, let's say I want to allow the user to enter a directory and image width when executing the command. Here's the updated code.&amp;lt;%$command = &quot;/usr/bin/php4 /var/www/myweb/image_resize.php --dir=/var/www/myeb/images --width=250&quot;;exec( &quot;$command &amp;gt; /dev/null &amp;amp;&quot;, $arrOutput );%&amp;gt;Notice that the -f option has been removed, since it appears that that option removes the first argument after the file name. Use the double dash to identify an argument with a key/value pair.Now, let's take a look at the code for image_resize.phpimage_resize.php&amp;lt;%$_ARG = arguments($_SERVER['argv']);$dir = $_ARG[&quot;dir&quot;];$width = $_ARG[&quot;width&quot;];function arguments($argv) {&amp;nbsp;&amp;nbsp;&amp;nbsp; $_ARG = array();&amp;nbsp;&amp;nbsp;&amp;nbsp; foreach ($argv as $arg) {&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if (ereg('--([^=]+)=(.*)',$arg,$reg)) {&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; $_ARG[$reg[1]] = $reg[2];&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; } elseif(ereg('-([a-zA-Z0-9])',$arg,$reg)) {&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; $_ARG[$reg[1]] = 'true';&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; }&amp;nbsp; return $_ARG;}print &quot;dir = $dir&amp;lt;br /&amp;gt;&quot;;print &quot;width = $width&amp;lt;br /&amp;gt;&quot;; %&amp;gt;image_resize.php outputdir = /var/www/myeb/imageswidth = 250Now you can setup any php script file to accept arguments through a command line. </description>
			<pubDate>Fri, 27 Jul 2007 14:56:00 PDT</pubDate>
		</item>
			
		<item>
			<title>Getting the Client?s IP Address</title>
			<link>http://www.sitecrafting.com/blog/getting-clients-ip-address/</link>
			<description>There are a number of reasons why you may want to capture a  web user's IP address. You may want to only allow certain IP addresses to view  your website, to allow only one vote per IP address, track unique clicks on  links and buttons, a location of web users on Google Maps using MASHUP, and whatever else you can think of.Below are a couple of examples to get the client's IP address in PHP and ASP.net, along with a couple of things to watch for.PHP Examples You could use the following to get the client's IP address &amp;lt;? $client_ip = $_SERVER[&quot;REMOTE_ADDR&quot;];// MAC Xserve may also have this server variable$client_ip = $_SERVER[&quot;PC-Remote-Addr&quot;];  ?&amp;gt;  To confirm which one to use, try looking at all the server variables by using print_r( $_SERVER). Another one to look for is $_SERVER[&quot;HTTP_PC_REMOTE_ADDR). It depends on upon on the server, IIS has a different set of names, apache has it's own set, etc.  ASP.net Examples Here are a couple of different methods in VB.net&amp;lt;% dim strClientIp AS string = &quot;&quot;  strClientIp = Me.Request.UserHostAddress  ' or use sever variables, just like the PHP examplesstrClientIp = Me.Request.ServerVariables(&quot;LOCAL_ADDR&quot;) %&amp;gt; If you're trying to capture the client IP inside of a class, use HttpContext. &amp;lt;% Public Class Client  Public IPAddress AS string  Sub New()   Me.IPAddress = HttpContext.Current.Request.UserHostAddress   End Sub  End Class %&amp;gt; What to watch for:  If the client's computer is inside of a network, the IP address will usually be the IP address of the router/firewall that connects to the Internet. Which could be a problem if your allowing one anonymous vote per IP address. If there are 20 computers within a network, then they could only have a total of one vote.Other server variables to look out for:The server variable HTTP_X_FORWARDED_FOR can also be used to find the client's IP address. Sometimes, this variable will have the computer's IP address inside of the network, which may be a worthless LAN address. However, this header can be easily faked and can also be a comma-separated list of IP addresses.  Another thing to watch for is how the router/firewall sends Internet traffic to the web server that is trying to capture the client's IP address. It's possible that the router/firewall has been setup to replace the REMOTE_ADDR or LOCAL_ADDR header values with a hard coded IP address. Setting the DMZ configuration for a router or running some code on the firewall can do this. For an example, download the Web Interface 4.0 troubleshooting guide and refer to the code on page 43.The last item is one that caught me. For a client's intranet site, I was attempting to validate an intranet user by capturing their IP address and validating it to a list of valid IP addresses. However, when browsing from the internet, from several computers at different locations, the IP address kept coming up as a LAN address, such as 10.0.1.15. This was because of the way the client's IT department setup the DMZ configuration.</description>
			<pubDate>Thu, 09 Aug 2007 16:14:00 PDT</pubDate>
		</item>
			
		<item>
			<title>AJAX & IE Caching Issues</title>
			<link>http://www.sitecrafting.com/blog/ajax-ie-caching-issues/</link>
			<description>I've been working on a project that involves using the jQuery javascript library and the Ajax methods. I've been happily using the Ajax jQuery.get() method to handle simple calls, such as a link that allows a user to view a list of words based on their selection, getting the contents of a tab when the user selects a tab, and finally getting the current weather after the person enters a new zip code, which is then saved to the database.  For the last operation, I got the JavaScript working great on Firefox and I thought it was working in Internet Explorer. However, after some testing, I noticed that the same weather data was being returned, even after a new zip code on the opposite coast was selected. Mmmm, I was rather baffled, especially since I've been using this jQuery.get() on the other functions, and encountered no problems like this.So, I started the Visual Studio 2005 debugger and viewed the page in Internet Explorer. I stepped through each line of code, so far so good. Then I pressed the edit weather preferences button, the form popped up, I changed the zip code, pressed the save button, and stepped through each line of code again. The weather data was updated to the database correctly. Next, the form closed and the Ajax jQuert.get() method was called. The debugger should have gone to the page for returning the weather data, but that did not occur. Instead, the data from the very first Ajax call was returned. That could only mean one thing, the infamous caching issue, which can be the bane of web developers and dynamic content.I quickly queried Google for &quot;jquery ie caching get&quot; and the very first entry, Ajax IE Caching Issues, had the solution. It turns out that, according to a comment from that article, the http specifications says any agent in the chain can cache the GET request, which Internet Explorer tends to do. The solution is very simple, either use a POST request instead or add a parameter to the querystring that changes. Some ideas could be adding a timestamp to the end of the url, adding a flag that changes each time the weather data is returned.  Here's the original code that caused IE to cache the response. Notice how the url is always &quot;ajax/weather.aspx?action=get&quot;, which causes IE to cache the response.$.get(&quot;ajax/weather.aspx&quot;, { action: &quot;get&quot; } , function(data) { getUsersWeatherCallBackHandler( data ); } );This code uses a timestamp to make the url unique each time the function is called. var tsTimeStamp= new Date().getTime(); $.get(&quot;ajax/weather.aspx&quot;, { action: &quot;get&quot;, time: tsTimeStamp} , function(data) { getUsersWeatherCallBackHandler( data ); } );This code simply uses a post request instead to get around the caching problem. $.post(&quot;ajax/weather.aspx&quot;, { action: &quot;get&quot; } , function(data) { getUsersWeatherCallBackHandler( data ); } );</description>
			<pubDate>Wed, 12 Sep 2007 16:52:00 PDT</pubDate>
		</item>
			
		<item>
			<title>PHP Patterns, Part II</title>
			<link>http://www.sitecrafting.com/blog/php-patterns-part-ii/</link>
			<description>  In this installment, we will be looking at two patterns that have been 'borrrowed' from Java. If you've had any development experience with J2EE, you are probably well aware of how handy Data Access Objects and Value Objects can be. If you haven't, don't fret! This article was written especially for you!If you've never heard these terms before, you may be wondering why I  have chosen to group them together within one article. The simple  explanation is ... well you'll see. For now just accept that they go  hand-in-hand, much like salt and pepper or peanut butter and jelly or  .Excited? Let's dig deeper...I. Data Access Objects (DAOs) and Value Objects (VOs), an IntroductionThese two classes comprise what is often called the &quot;model&quot; layer of the MVC (Model View Control) model. If you aren't familiar with this concept do yourself a huge favor and read up on it - it will save you and your peers quite a bit of time further down the road on any project.The DAO is basically the object you use to query your data source within your application. Rather than sprinkling SQL queries throughout your code, it allows you to encapsulate everything related to accessing your database within a single class. That way, if you ever change databases or need to tweak a query, you can do it in one place that's easy to find. Think of it like a filing cabinet that keeps all your papers from floating all over your desk.The VO does what you might expect a &quot;value object&quot; to do - it holds values. It provides you with a nifty object to pass around your application and typically contains a single row of information from your database. Again the goal here is to simplify the process of handling data retrieved from the database.II. The VO, A Closer LookCreating a VO is probably one of the simplest classes you will ever create. It basically mirrors a table in your database, with setter and getter methods for each field in that table. First, let's pretend you have a simple database table called &quot;users&quot; that has the following columns:iduserpassUsing this information, we can create our very first Value Object:class&amp;nbsp;UserVO&amp;nbsp;{&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;protected&amp;nbsp;$id;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;protected&amp;nbsp;$username;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;protected&amp;nbsp;$password;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;function&amp;nbsp;setId($id)&amp;nbsp;{&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;$this-&amp;gt;id&amp;nbsp;=&amp;nbsp;$id;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;function&amp;nbsp;getId()&amp;nbsp;{&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;$this-&amp;gt;id;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;function&amp;nbsp;setUsername($username)&amp;nbsp;{&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;$this-&amp;gt;username =&amp;nbsp;$username;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;function&amp;nbsp;getUsername()&amp;nbsp;{&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;$this-&amp;gt;username;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;function&amp;nbsp;setPassword($password)&amp;nbsp;{&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;$this-&amp;gt;password =&amp;nbsp;$password;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;function&amp;nbsp;getPassword()&amp;nbsp;{&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;$this-&amp;gt;password;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}}Pretty simple and to the point, huh? You may be wondering why I have gone through and written explicit setters and getters for each value, rather than just making two generic functions (or simply making the class variables public). The primary reason for this has more to do with coding standards than anything else. It is usually good practice to prevent class variables from being accessed directly. This way you don't have to anticipate what might happen ouside the class. By declaring functions for each specific value, you have more control over the use of each value as well. For instance, where appropriate, you could use the addslashes() and stripslashes()&amp;nbsp; functions to automatically massage data before it is inserted into the database.II. The DAO, A Closer LookNow that we have created our first VO, let's take a look at its partner in crime, the DAO. Let's take a look at some code before we start picking it apart:class&amp;nbsp;UserDAO&amp;nbsp;{&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;protected&amp;nbsp;var&amp;nbsp;$connect;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;protected&amp;nbsp;var&amp;nbsp;$db;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//&amp;nbsp;Attempts&amp;nbsp;to&amp;nbsp;initialize&amp;nbsp;the&amp;nbsp;database&amp;nbsp;connection&amp;nbsp;using&amp;nbsp;the&amp;nbsp;supplied&amp;nbsp;info.&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;function&amp;nbsp;UserDAO($host,&amp;nbsp;$username,&amp;nbsp;$password,&amp;nbsp;$database)&amp;nbsp;{&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;$this-&amp;gt;connect&amp;nbsp;=&amp;nbsp;mysql_connect($host,&amp;nbsp;$username,&amp;nbsp;$password);&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;$this-&amp;gt;db&amp;nbsp;=&amp;nbsp;mysql_select_db($database);&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//&amp;nbsp;Executes&amp;nbsp;the&amp;nbsp;specified&amp;nbsp;query&amp;nbsp;and&amp;nbsp;returns&amp;nbsp;an&amp;nbsp;associative&amp;nbsp;array&amp;nbsp;of&amp;nbsp;reseults.&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;protected&amp;nbsp;function&amp;nbsp;execute($sql)&amp;nbsp;{&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;$res&amp;nbsp;=&amp;nbsp;mysql_query($sql,&amp;nbsp;$this-&amp;gt;connect)&amp;nbsp;or&amp;nbsp;die(mysql_error());&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if(mysql_num_rows($res)&amp;nbsp;&amp;gt;&amp;nbsp;0)&amp;nbsp;{&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;for($i&amp;nbsp;=&amp;nbsp;0;&amp;nbsp;$i&amp;nbsp;&amp;lt;&amp;nbsp;mysql_num_rows($res);&amp;nbsp;$i++)&amp;nbsp;{&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;$row&amp;nbsp;=&amp;nbsp;mysql_fetch_assoc($res);&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;$userVO[$i]&amp;nbsp;=&amp;nbsp;new&amp;nbsp;UserVO();&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;$userVO[$i]-&amp;gt;setId($row[id]);&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;$userVO[$i]-&amp;gt;setUsername($row[username]);&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;$userVO[$i]-&amp;gt;setPassword($row[password]);&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;$userVO;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//&amp;nbsp;Retrieves&amp;nbsp;the&amp;nbsp;corresponding&amp;nbsp;row&amp;nbsp;for&amp;nbsp;the&amp;nbsp;specified&amp;nbsp;user&amp;nbsp;ID.&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;function&amp;nbsp;getByUserId($userId)&amp;nbsp;{&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;$sql&amp;nbsp;=&amp;nbsp;&quot;SELECT&amp;nbsp;*&amp;nbsp;FROM&amp;nbsp;users&amp;nbsp;WHERE&amp;nbsp;id=&quot;.$userId;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;$this-&amp;gt;execute($sql);&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//&amp;nbsp;Retrieves&amp;nbsp;all&amp;nbsp;users&amp;nbsp;currently&amp;nbsp;in&amp;nbsp;the&amp;nbsp;database.&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;function&amp;nbsp;getUsers()&amp;nbsp;{&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;$sql&amp;nbsp;=&amp;nbsp;&quot;SELECT&amp;nbsp;*&amp;nbsp;FROM&amp;nbsp;users&quot;;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;$this-&amp;gt;execute($sql);&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//Saves&amp;nbsp;the&amp;nbsp;supplied&amp;nbsp;user&amp;nbsp;to&amp;nbsp;the&amp;nbsp;database.&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;function&amp;nbsp;save($userVO)&amp;nbsp;{&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;$affectedRows&amp;nbsp;=&amp;nbsp;0;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if($userVO-&amp;gt;getId()&amp;nbsp;!= &quot;&quot;)&amp;nbsp;{&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;$currUserVO&amp;nbsp;=&amp;nbsp;$this-&amp;gt;getByUserId($userVO-&amp;gt;getId());&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//&amp;nbsp;If&amp;nbsp;the&amp;nbsp;query&amp;nbsp;returned&amp;nbsp;a&amp;nbsp;row&amp;nbsp;then&amp;nbsp;update,&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//&amp;nbsp;otherwise&amp;nbsp;insert&amp;nbsp;a&amp;nbsp;new&amp;nbsp;user.&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if(sizeof($currUserVO)&amp;nbsp;&amp;gt;&amp;nbsp;0)&amp;nbsp;{&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;$sql&amp;nbsp;= &quot;UPDATE users SET &quot;.&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &quot;username='&quot;.$userVO-&amp;gt;getUsername().&quot;', &quot;.&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &quot;password='&quot;.$userVO-&amp;gt;getPassword().&quot;' &quot;.&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &quot;WHERE id=&quot;.$userVO-&amp;gt;getId();&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;mysql_query($sql,&amp;nbsp;$this-&amp;gt;connect)&amp;nbsp;or&amp;nbsp;die(mysql_error());&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;$affectedRows&amp;nbsp;=&amp;nbsp;mysql_affected_rows();&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;else&amp;nbsp;{&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;$sql&amp;nbsp;=&amp;nbsp;&quot;INSERT&amp;nbsp;INTO&amp;nbsp;users&amp;nbsp;(username,&amp;nbsp;password)&amp;nbsp;VALUES('&quot;.&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;$userVO-&amp;gt;getUsername().&quot;', &quot;.&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;$userVO-&amp;gt;getPassword().&quot;')&quot;.&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;mysql_query($sql,&amp;nbsp;$this-&amp;gt;connect)&amp;nbsp;or&amp;nbsp;die(mysql_error());&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;$affectedRows&amp;nbsp;=&amp;nbsp;mysql_affected_rows();&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;$affectedRows;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//&amp;nbsp;Deletes&amp;nbsp;the&amp;nbsp;supplied&amp;nbsp;user&amp;nbsp;from&amp;nbsp;the&amp;nbsp;database.&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;function&amp;nbsp;delete($userVO)&amp;nbsp;{&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;$affectedRows&amp;nbsp;=&amp;nbsp;0;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//&amp;nbsp;Check&amp;nbsp;for&amp;nbsp;a&amp;nbsp;user&amp;nbsp;ID.&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if($userVO-&amp;gt;getId()&amp;nbsp;!= &quot;&quot;)&amp;nbsp;{&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;$currUserVO&amp;nbsp;=&amp;nbsp;$this-&amp;gt;getByUserId($userVO-&amp;gt;getId());&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//&amp;nbsp;Otherwise delete a user.&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if(sizeof($currUserVO)&amp;nbsp;&amp;gt;&amp;nbsp;0)&amp;nbsp;{&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;$sql&amp;nbsp;=&amp;nbsp;&quot;DELETE&amp;nbsp;FROM&amp;nbsp;users&amp;nbsp;WHERE&amp;nbsp;id=&quot;.$userVO-&amp;gt;getId();&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;mysql_query($sql,&amp;nbsp;$this-&amp;gt;connect)&amp;nbsp;or&amp;nbsp;die(mysql_error());&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;$affectedRows&amp;nbsp;=&amp;nbsp;mysql_affected_rows();&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;$affectedRows;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}}The Data Access Object consists a handful of basic methods. First is the constructor, which basically initiates the connection to the database. You pass it the database connection info and credentials, and it takes care of the rest. Next comes the getter functions, which basically represent a collection of the queries used to access the database. For example, you might have a getByUserId() method that returns the UserVO we created above filled with the corresponding data for a particular user ID.Following the getters is the save() function, which essentially does what you would imagine it does. You pass a VO to it and it will either update an existing record if it finds one that matches the VO or will insert a new row. I'll bet you're thinking what I thought the first time I learned of this function: &quot;holy cow! that just reduced 8+ lines of code down to one!&quot;Finally we have the big scary delete() function that, you guessed it, deletes a record from the database. To make it slightly less scary, it requires that you pass it a VO that matches a database row exactly. This will force you to have first retrieved the row you want to delete before calling this method. This helps prevent the accidental deletion of a record.And there you have it! Together these two classes can save you a tremendous amount of effort and time, makes it much easier to maintain say 6-12 months from now when you have completely forgotten how you wrote that file manager script.A side note: If you live in fear of having to write these classes for databases with 100 tables that have 200 columns each, don't despair! With the help of Google you can find countless PHP DAO and VO generators that will build the code for you (take a look here for example). Some will do it based on existing database tables, and others will do it based on what you tell them. They might still require some tweaking afterwards, but at least that way the hard part would be done and over with.</description>
			<pubDate>Wed, 14 Nov 2007 15:48:00 PST</pubDate>
		</item>
			
		<item>
			<title>Powered by Coffee</title>
			<link>http://www.sitecrafting.com/blog/powered-by-coffee/</link>
			<description>We all know that design and development firms are really run on caffeine. Coffee, energy drinks, tea, and even energy beers now. I usually can be found with 2-3 coffee cups on my desk throughout the week but I will never forget about one developer who I worked with. Each day he would purchase 3-4 drip coffees from Starbucks and then would stack the cups creating a wall about 5 layers high creating his own cubical wall.&amp;nbsp; It was all fine until one day when a few of them turned sour and his office buddy/management put an end to it. What do you prefer to drink while working? I'm usually a fan of mochas or red bull. Have any ridiculous stories about coworkers caffeine habits?</description>
			<pubDate>Mon, 29 Oct 2007 10:48:00 PDT</pubDate>
		</item>
			
		<item>
			<title>Why is AOL denying email?</title>
			<link>http://www.sitecrafting.com/blog/aol-denying-email/</link>
			<description>  Email debugging is often frustrating and time consuming. There could be numerous reasons why the email you attempted to send out through your code fails: routing issues, firewalls, company servers blacklisting or blocking incoming emails, spam filters, bad DNS records, and email header requirements. The last one can definetely lead to infinite frustration, as I have experienced in the past, and was recently the cause of why AOL was denying emails generated by the php mail() function.  I have learned that some of the free email hosting sites require certain headers be present in the email. If the header is missing something or is malformed, the hosting vendor blocks that email. It also seems like there is no acceptable standard, which increases the fickle nature of email. However, it should be noted that there are some php classes that appear to handle all those cases, such as the PHP PEAR MAIL package and PHP Mailer.For my current case, AOL requires a sender envelope wrapper before it will accept an email. In order to do this, a parameter needs to be added to the mail() function. $to = &quot;ken@kensblog.com&quot;;$subject = &quot;Why is AOL blocking emails?&quot;;$message = &quot;Because AOL requires a sender envelop wrapper&quot;;$headers = &quot;From: postmaster@domain.com\nMIME-Version: 1.0\nContent-type: text/plain; charset=iso-8859-1\n&quot;;$parameters = &quot;-f postmaster@domain.com&quot;;mail($to, $subject, $message, $headers, $parameters );The $parameters field is key to getting an email delivered to AOL. Any email delivery errors are sent to the email address, postmaster@domain.com. In addition, the email address must be a valid domain, otherwise AOL will deny the incoming email.This link shows a list of parameters that you could add to the $parameters field and it also explains the &quot;-f&quot; parameter.Below is an example of a valid header that should be accepted by AOL.X-AOL-UID: 3317.422461254 'Added by AOLX-AOL-DATE: Fri, 30 Nov 2007&amp;nbsp; 1:05:25 PM Eastern Standard Time 'Added by AOLReturn-Path: &amp;lt;usweb@airstream.com&amp;gt; 'Added by the -f parameterReceived: from rly-da03.mx.aol.com...Received: from servername.local...Received: by servername.local ...To: ken@kensblog.comSubject: Why is AOL blocking emails?From: postmaster@domain.comReply-To: postmaster@domain.comMIME-Version: 1.0Content-Type: text/plain; charset=ISO-8859-1; Message-Id: &amp;lt;20071130180521.69A6EB69CDF@servername.local&amp;gt;Date: Fri, 30 Nov 2007 10:05:21 -0800 (PST)X-AOL-IP: 169.189.1.19X-AOL-SCOLL-AUTHENTICATION: listenair ; SPF_helo : nX-AOL-SCOLL-AUTHENTICATION: listenair ; SPF_822_from : &amp;lt;postmaster@domain.com&amp;gt;X-Mailer: Unknown (No Version)</description>
			<pubDate>Fri, 30 Nov 2007 13:05:00 PST</pubDate>
		</item>
			
		<item>
			<title>PHP Patterns, Part III</title>
			<link>http://www.sitecrafting.com/blog/php-patterns-part-iii/</link>
			<description>The Template Object (or TO) is a design pattern of my own that I developed to fill the role of the View layer in the MVC model. As you have probably figured out, the purpose of the TO is to handle everything related to the user interface. The idea here is to separate the interface as much as possible from the rest of the application, so that we could do a complete rewrite of an application without ever touching (or accidentally &quot;breaking&quot;) the view portion.This object was designed around a template model, in which the HTML/CSS is stored completely independent of the template object. Using this approach, the appearance of the web application and how it operates are completely decoupled. Both the view layer and the rest of the application can be completely replaced without affecting each other.So before I bore you to death with my talking, let's take a look at some code. Below is some sample code for an abstract TemplateObject class, and then a basic FormTO class that would be useful for a basic web form:/**&amp;nbsp;*&amp;nbsp;Abstract&amp;nbsp;TemplateObject&amp;nbsp;Class&amp;nbsp;*/abstract&amp;nbsp;class&amp;nbsp;TemplateObject&amp;nbsp;{&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;abstract&amp;nbsp;function&amp;nbsp;render();&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;abstract&amp;nbsp;function&amp;nbsp;setTemplate($template);}/**&amp;nbsp;*&amp;nbsp;Generic&amp;nbsp;Form&amp;nbsp;Template&amp;nbsp;Object&amp;nbsp;*/class&amp;nbsp;FormTO&amp;nbsp;extends&amp;nbsp;TemplateObject&amp;nbsp;{&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;protected&amp;nbsp;$template;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;protected&amp;nbsp;$method;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;protected&amp;nbsp;$action;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;protected&amp;nbsp;$name;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;/**&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;*&amp;nbsp;Default&amp;nbsp;Constructor&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;*&amp;nbsp;@param&amp;nbsp;string&amp;nbsp;$method&amp;nbsp;the&amp;nbsp;form&amp;nbsp;method&amp;nbsp;(post/get)&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;*&amp;nbsp;@param&amp;nbsp;string&amp;nbsp;$action&amp;nbsp;the&amp;nbsp;form&amp;nbsp;action&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;*&amp;nbsp;@param&amp;nbsp;string&amp;nbsp;$name&amp;nbsp;the&amp;nbsp;name&amp;nbsp;of&amp;nbsp;the&amp;nbsp;form&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;*&amp;nbsp;@param&amp;nbsp;string&amp;nbsp;$template&amp;nbsp;the&amp;nbsp;location&amp;nbsp;of&amp;nbsp;the&amp;nbsp;template&amp;nbsp;file&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;*/&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;function&amp;nbsp;__construct($method&amp;nbsp;=&amp;nbsp;post,&amp;nbsp;$action&amp;nbsp;=&amp;nbsp;,&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;$name&amp;nbsp;=&amp;nbsp;,&amp;nbsp;$template&amp;nbsp;=&amp;nbsp;)&amp;nbsp;{&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;$this-&amp;gt;template&amp;nbsp;=&amp;nbsp;$template;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;$this-&amp;gt;method&amp;nbsp;=&amp;nbsp;$method;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;$this-&amp;gt;action&amp;nbsp;=&amp;nbsp;$action;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;$this-&amp;gt;name&amp;nbsp;=&amp;nbsp;$name;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;/**&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;*&amp;nbsp;Renders&amp;nbsp;the&amp;nbsp;page&amp;nbsp;using&amp;nbsp;the&amp;nbsp;currentlyl&amp;nbsp;stored&amp;nbsp;values.&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;*/&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;function&amp;nbsp;render()&amp;nbsp;{&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;try&amp;nbsp;{&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;include($this-&amp;gt;template);&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;catch&amp;nbsp;Exception($ex)&amp;nbsp;{&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(The&amp;nbsp;specified&amp;nbsp;template&amp;nbsp;was&amp;nbsp;invalid.);&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;function&amp;nbsp;setTemplate($template)&amp;nbsp;{&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;$this-&amp;gt;template&amp;nbsp;=&amp;nbsp;$template;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;function&amp;nbsp;setMethod($method)&amp;nbsp;{&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;$this-&amp;gt;method&amp;nbsp;=&amp;nbsp;$method;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;protected&amp;nbsp;function&amp;nbsp;printMethod()&amp;nbsp;{&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print($this-&amp;gt;method);&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;function&amp;nbsp;getMethod()&amp;nbsp;{&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;$this-&amp;gt;method;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;function&amp;nbsp;setAction($action)&amp;nbsp;{&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;$this-&amp;gt;action&amp;nbsp;=&amp;nbsp;$action;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;protected&amp;nbsp;function&amp;nbsp;printAction()&amp;nbsp;{&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print($this-&amp;gt;action);&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;function&amp;nbsp;getAction()&amp;nbsp;{&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;$this-&amp;gt;action;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;function&amp;nbsp;setName($name)&amp;nbsp;{&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;$this-&amp;gt;name&amp;nbsp;=&amp;nbsp;$name;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;protected&amp;nbsp;function&amp;nbsp;printName()&amp;nbsp;{&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print($this-&amp;gt;name);&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;function&amp;nbsp;getName()&amp;nbsp;{&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;$this-&amp;gt;name;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}}As you can see this is a fairly simple setup. We have a simple Template Object that we can use to set various values for the page it reperesents. It essentially acts as an interface to the actual web page. Now lets take a look at what a template file might look like. Remember, the added felxibility here is that the template file can do anything it wants with the data that has been stored in the TO. It has full control over how the information is presented. For illustrative purposes, I will make my example as simple as possible:&amp;lt;form method=&quot;&amp;lt;?php $this-&amp;gt;printMethod() ?&amp;gt;&quot; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;action=&quot;&amp;lt;?php $this-&amp;gt;printAction() ?&amp;gt;&quot;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;name=&quot;&amp;lt;?php $this-&amp;gt;printName() ?&amp;gt;&quot;&amp;gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;!-- FORM CONTENT WOULD GO HERE --&amp;gt;&amp;lt;/form&amp;gt;I'm sure you can imagine how easy it becomes to expand upon this simple design to build a very flexiple and complex web application in very little time. Using this approach, a developer could build a site without any concrete design in place, using a simple layout that could easily be replaced further down the road without rewriting or refactoring any code. This also allows designers to build an interface without having to understand the backend structure of the application. Pretty handy if you ask me!</description>
			<pubDate>Tue, 29 Jan 2008 15:15:00 PST</pubDate>
		</item>
			
	</channel>
</rss>
		
