Are you a programmer? Do you remember when you first started? More specifically, do you remember any of the code you wrote back then? If you are anything like me, you remember it being a fun time, full of new problems to solve and experiences to gain. Even though my memories of this time were positive, looking back on the code I wrote is a cringe-inducing experience.

Natural Selection

When I first started programming with Elemental Blend, PHP 5 was brand spanking new, the MySQL version of choice was 3.23.x, and Apache 1.x was standard. If you think about this for a bit, I am sure you can imagine some of the code that was written (we will get more into this later). Did your old coding practices survive over time? Ours sure didn't. While some basic principles are the same, the execution and process is completely different. What I found most interesting is the patterns our code evolution created. You can clearly see weak areas and what changed overtime to address them.

Struggle for Life

Let's look at some blocks of sample code. Each block represents a coding change that took place and should give you a visual for the evolution of code that happened within Elemental Blend. We will use a news CMS item as the example.

Copy Paste Method

<?php

$conn = mysql_connect("localhost", "USERNAME", "PASSWORD");
  mysql_select_db("DB_NAME",$conn);

$add_news = false;  // initialize vars
$news_id_EDIT = 0;
$archive_news = false;
$remove_news = false;

if ($_POST['add_news_item']) // this is if someone posts an ADD form.. and it will insert news into database
{ 
	$title = $_POST['title'];
	$p_date = $_POST['updateY']."-".$_POST['updateM']."-".$_POST['updateD'];
	$a_date = '0000-00-00';
	$teaser = $_POST['teaser'];
	$body = $_POST['body'];

	$query="INSERT INTO news VALUES(\"$title\", \"$p_date\", \"$a_date\", \"$teaser\", \"$body\") ";

	mysql_query($query,$conn)
		or die("SQL Error in QUERY:  ".mysql_error());

	$add_news = true;	
}

?>
/* HTML for add form would go here */

Reasons for Evolution

  • Nothing could be reused unless you copy and pasted
  • Database login credentials are inline on the page (and subsequently every page that uses the database)
  • Variables declared that are not even used (I am looking at you $archive_news and $remove_news)
  • Unnecessary variable assignment. Why did I save $_POST['title'] into $title?
  • User input was not sanitized (give me a little break, it was before I even knew what SQL injection was)
  • Inconsistent indenting, spacing, and commenting
  • I have no idea why $a_date was set to 0000-00-00 or why it wasn't being used
  • If the query failed, I displayed the query in the error message for all users to see
  • No session authentication... meaning anyone could access this page and add news
  • Processing the form on top of the HTML page

Function Decomposition Method

<?php
include_once("../include/config.inc.php");
include_once("../include/news.inc.php");

$conn = dbConnect($dbHost, $dbUser, $dbPass, $dbName) or die("Could not connect to the database");
checkStatus('medium'); // will redirect if not admin or admin

// process add form	
if(!empty($_POST['newsTitle']))
{//process new news item

	$catID = $_POST['newsCategory'];
	$title = $_POST['newsTitle'];
	$teaser = $_POST['newsTeaser'];
	$body = $_POST['newsBody'];

	if(addNews($title,$teaser,$body,$catID))
	{
		$message = "Successfully Added.";
	}
	else
	{
		$error = "Could not create news, please try again.";
	}
}
?>
/* HTML for add form would go here */

Changes from Copy and Paste Method

  • Functioned out database credentials so they are in one place
  • Included simple session based authentication - checkStatus('medium')
  • Database queries has been functioned out and can be reused
  • Better error handling by showing an error message instead of the query

Reasons for Evolution

  • include_once is not a function, yet I use it like one
  • Continued using unnecessary variable assignment
  • Inconsistent use of single and double quotes
  • Commenting is still not very good
  • Still vulnerable to SQL injections
  • Still difficult to maintain after launch
  • Still processing the form on top of the HTML page

Object Oriented Method

<?php
	// parent classes
	include_once('../class/database.class.php');
	include_once('../class/site.class.php');
	include_once('../class/admin.class.php');
	include_once('../class/news.class.php');
	include_once('../class/format.class.php');	

	// instantiate classes
	$site = new site;
	$db = new database();
	$admin = new admin();

	// check session
	$admin->checkSession();

	// connect to database
	$conn = $db->connect();

	// process news form
	if(!empty($_POST['title']))
	{
		$news = new news();
		$format = new format();

		$news->setTitle($format->cleanUp($_POST['title']));
		$news->setTeaser($format->cleanUp($_POST['teaser']));
		$news->setBody($format->cleanUp($_POST['body']));
		$news->setURL($format->cleanUp($_POST['url']),$format->cleanUp($_POST['urlTitle']));
		$news->add();
		$msg = 'Successfully Added.';		
	}	
	// end news form

	// close database
	$db->disconnect($conn);	
?>
/* HTML for add form would go here */

Changes from Functional Decomposition Method

  • Functions are replaced by classes
  • Comments makes more sense and split the code into readable blocks
  • User input is finally sanitized using $format->cleanUp()
  • Clearer naming of items in general

Reasons for Evolution

  • include_once is not a function, yet I continued it like one
  • Lots of include_once statements at the top of the file
  • Processing the form on top of the HTML page

Object Oriented MVC method

<?php
/*
	Controller:	news
	Author:	elementalblend.com (brandon burkett)

	Desc:	Class will process all forms related to news.

	Note:	All controllers must extend blendCore_controller.  They can be semi-custom
		per project or completely custom per project.  The controller will handle
		any form processing.
*/
class blendController_news extends blendCore_controller
{
	/* properties */
	protected $imgPath;
	protected $docPath;
	protected $defaultResults;
	protected $imgSize;

	/* constructor */
	public function __construct()
	{		
		parent::__construct();
		$this->imgPath = 'newsImages/';
		$this->docPath = 'newsDocs/';
		$this->defaultResults = 25;
		$this->imgSize = array('w'=>415,'h'=>415);
	}

	/* get methods */
	public function getImgPath()
	{
		return $this->imgPath;
	}

	public function getDocPath()
	{
		return $this->docPath;
	}

	/* admin methods */	
	public function add(blendCustom_mysql $db)
	{
		// instantiate objects
		$newsDB = new blendModel_news($db);
		$catDB = new blendModel_category($db);
		$session = new blendCore_session();
		$browser = new blendCore_browser();
		$form = new blendCore_form();

		// process form if token matches
		if(!empty($this->params['token']))
		{
			if($session->newsAddFormToken == $this->params['token'])
			{
				// sanitize input								
				$newsDB->news_title = $form->sanitize($this->params['title']);
				$newsDB->news_teaser = $form->sanitize($this->params['teaser']);
				$newsDB->news_desc = $form->sanitize($this->params['body']);
				$newsDB->cat_id = $form->sanitize($browser->decodeURI($this->params['cat']));
				$newsDB->admin_id = $session->admin_id;				

				// save record
				if($newsDB->save())
				{					
					$msg = 'Successfully Added News';
					$error = '0';					
				}
				else
				{
					$msg = 'Error Adding News';
					$error = '1';
				}

				// redirect to list vew
				$id = $browser->abstractURI($newsDB->news_id);
				$browser->redirect('admin/lsNews.php?l='.$session->l.'&p='.$session->p.'&str='.$session->str.
					'&msg='.urlencode($msg).'&error='.$error.'&news_id='.$id);
			}
		}
		// end process form

		// get categories
		$catDB->cat_archive = 'N';
		$catDB->cat_type = 'news';
		$catDB->order = 'cat_order';
		$catRS = $catDB->find();

		// build return object for add form view
		$results = new stdClass();
		$results->catRS = $catRS;

		return $results;		
	}
}
?>

Changes from Object Oriented Method

  • Classes are split into models (database related items) and controllers (form and view related items)
  • HTML files are separated from form processing code
  • Many common/recurring tasks are held in the controller abstract class
  • Added a unique session token to help reduce spam and accidental double submissions
  • Communication between the controller and the view is done with a return object
  • All news related form processing and display calls are in one file
  • A bootstrap file (not shown) handles all the include_once processing, so there is no need for multiple include_once calls

Preservation of Favored Races

The Object Oriented MVC method is what we still use today. We even built a micro-framework around MVC that forces consistency and really helps with the maintainability of our projects. The above pattern of coding changes is how the code evolved at Elemental Blend and your code may have taken a completely different path. We found that each site has many common things that always need to be done, separating out these common items into models (for database interaction), controllers (for view and form processing), and views (for HTML display) greatly increases code reusability and maintainability.

I would be interested and hearing your evolution of code.


COMMENTS

Sorry admin - my post is test

jambbuigmahix

Post a Comment

  1. (you can post your real name, a username or leave blank for an anonymous post)

Comments support these basic HTML tags: <a>, <strong>, <em>