Mark’s Site

Pensieve for coding and golf :-)

Decorator Pattern

May 17th, 2008

The decorator pattern allows us to extend (decorate) the functionality of a class at runtime (wikipedia).

I was largely self taught in programming, so this description sounded just like classes, subclasses, and the whole concept of inheritance, but this is a different concept (related, but different). A good example I found made this easier to understand, so I thought it relevant to share the ‘easy to learn’ stuff around ;-)

The decorator patterns works by creating a ‘decorator’ class that wraps the original class. This effect is achieved by passing the original object as a parameter to the constructor of the decorator class. You specify any extended (or new) functionality in the decorator class, and any original class functionality can be called as usual (don’t worry, there’s a good example to follow soon).

So straight away I’m asking:

“Why the hell would I want to do that when I can use inheritance and have a neat class/subclass solution that I am more comfortable with?”

and

“What is the difference between the decorator pattern and standard inheritance?”

Well, inheritance happens at compile time, whereas decorating provides the original classes with the additional (decorated) functionality at runtime.

Wow, that sounds real important. Who cares! I use PHP, compile and runtime mean bugger all to me. Give me a reason why I would use this method! It took this example to finally get the message across to me :-)

Real world example

“As an example, consider a window in a windowing system. To allow scrolling of the window’s contents, we may wish to add horizontal or vertical scrollbars to it, as appropriate. Assume windows are represented by instances of the Window class, and assume this class has no functionality for adding scrollbars. We could create a subclass ScrollingWindow that provides them, or we could create a ScrollingWindowDecorator that merely adds this functionality to existing Window objects. At this point, either solution would be fine.

Now let’s assume we also wish the option to add borders to our windows. Again, our original Window class has no support. The ScrollingWindow subclass now poses a problem, because it has effectively created a new kind of window. If we wish to add border support to all windows, we must create subclasses WindowWithBorder and ScrollingWindowWithBorder. Obviously, this problem gets worse with every new feature to be added. For the decorator solution, we need merely create a new BorderedWindowDecorator—at runtime, we can decorate existing windows with the ScrollingWindowDecorator or the BorderedWindowDecorator or both, as we see fit.

Another good example of where a decorator can be desired is when there is a need to restrict access to an object’s properties or methods according to some set of rules or perhaps several parallel sets of rules (different user credentials, etc). In this case instead of implementing the access control in the original object it is left unchanged and unaware of any restrictions on its use, and it is wrapped in an access control decorator object, which can then serve only the permitted subset of the original object’s interface.” (wikipedia)

Downgrade Gracefully

May 7th, 2008

We often rely on external connections in our coding. One such example is a connection to our database. Others might be SOAP connections, or any external API like Facebook.

Do you know what happens to your code if the database becomes unavailable, or the external API just drops off the face of the net? I mean, you’ve taken the code examples from someone else that had “or die(’no database connection’)” …but have you actually tested what this does and how it looks to a user should this connection become unavailable?

The best thing you can do is provide incorrect database parameters to your website and sit back and just see what happens. Try an incorrect host, then try incorrect login details to the correct host. Then try the wrong database name. Try it all, and see how your application performs. You should always present your USER with a pleasant message, and not some PHP generated error message with half your page cut off from a loading problem. So either create a page dedicated to such situations where you can redirect to in these instances, or just make sure you present a friendly error message on the page where the problem occurred, ensuring the rest of the page design doesn’t get cut off unexpectedly thanks to the error that’s occurred.

Doing this is important for your users. If they see a bad error message instead of your site, they might not come back. And also importantly is the type of error messages you show in your coding. PHP fatal errors with databases might show your database name, and login details. We wouldn’t want to give a hacker the favour of all the hard work would we? So play smart and handle your errors!

Screen Design

May 4th, 2008

When I first started web development, one thing I was always torn on was how to design my screens. I had loads of information that I needed to display it all in some meaningful, easy to search format. After many years of trial and error, I’ve now got my ‘template’ screens that I use for all my content management screens.

What follows is how I layout standard screens in my system. You may love it, you may hate it, how you choose to layout your screens is completely up to you, but hopefully you will see the reasoning behind my screen layouts and adopt a similar approach for yourself.

I find the combination of list and add/update screens to be sufficient for usability purposes. Some developers like to include an intermediary ‘info’ screen before users can get to the update screen, but I did not feel this useful for my CMS, so I exclude the info screen and go straight to the update screen when a record is clicked in the list screen.

List Screen

My list screens show me all of my records, paginated, with enough information on screen for users to find what they are looking for. I also have search available per list screen to filter results.

You can see on this list screen you have an ‘add’ button, allowing you to get to the add screen FROM the list screen. I find this useful for cutting down on menu items. I just have a ‘Manage Page Categories’ menu item, which gets the user to the list screen where the user can then choose to add a new record, or click an existing record.

I’ve mentioned the paging, this is very important, otherwise you’ll have a massive list screen and your system won’t scale :-) By this, I don’t just mean your system won’t scale in terms of usability, but imagine you suddenly have a 1000+ list of records. That’s a massive amount of pressure to put on one screen. The performance of your system would suffer. So that is the primary reason we use paging, so cut down on the SQL results returned from the database, so as not to affect system performance. As paging is outside the scope of this article, I’ll discuss paging in a future article.

I’ve got Select All javascript options and actions to set active/hidden and delete, allowing you to bulk hide/activate records. I’ve found this useful when importing records.

An example list screen is shown below:

Add/Update Screen

My add/update screens are one PHP page that dynamically changes depending on whether I am adding a new record, or updating an existing record. I add buttons to the top and bottom of the form. For small forms this may seem a bit unnecessary, but I still find myself clicking top and bottom buttons, so I’ve decided to leave them in.

This is an example add screen:

All About Commenting

May 2nd, 2008

test test  estset

testsetset

test

It can be confusing when you start developing in a new language to know about the rules for commenting. For example when I started coding in VB.NET, I was shocked to find no way of bulk commenting out code. Every line needed to be commented out. Crazy stuff.

Luckily for us, we’re working with PHP, and PHP has been developed with bulk commenting options, which I find to be very handy, especially when debugging.

Single Line Commenting

To my knowledge, there are two ways of doing single line comments in PHP. The first is with two forward slashes as follows:

<?php
// echo 'this is text that is going to be commented out';
?>

The second, which is fast becoming my preference, is a single hash character, as follows (I prefer this because I find it faster to type, you will have to find your preference):

<?php
# echo 'this is text that is going to be commented out';
?>

Multiple Line (Bulk) Commenting

Single line commenting is all well and good, but there are times when bulk commenting can be very useful too. I find bulk commenting to be useful in two situations:

1. When providing summary text to functions or classes

I find this looks neater than a bunch of single line comments. Again, just my preference.

<?php
/*
Author: Me
Summary: This function does some really cool stuff.
To use this function, do this, this and this.
Parameter var1: An integer
*/
function do_something($var1){
   //do stuff in here
}
?>

2. When debugging code
When I need to find the source of an error in my code, I’ll use bulk commenting to hide sections of code, until I’ve found where my error is happening. Very useful :-)

Staging Environments

May 1st, 2008

It’s so important to have a separate staging environment, so you can make changes (major or minor) and test the results BEFORE releasing to your live/production server. And for applications where development is an ongoing process, have a third environment purely for testing before releasing to live.

It’s important to have a SEPARATE staging environment, running on the same hardware and software configuration as your live environment. Small configuration changes could mean the difference between something passing testing, or failing testing. Yes it’s double the work to setup and release, but for an application that has a user base, it is critical to never have downtime or errors showing on your live system.

Why So Important?

Errors may give users an insight into your application that you may not wish them to know about. For example, a SQL query is setup poorly and pulls a fatal error. Without realising, you forgot to catch this error and the error displays some of your SQL code, including your database name and table/column information, so a serious security threat.

So staging environments are important for testing and making sure no unexpected bugs get to your live environment.

The Process

  1. User requests new functionality
  2. You accept request as a valid addition to your system
  3. Create new functionality on your staging environment
  4. You test the changes you made
  5. Get the changes fully tested by someone else, ‘fully’ meaning any area of the system that could have been affected by the changes
  6. When staging is tested and proven to be fine, release to live
  7. You test live to make sure the release to live went smoothly

Lightweight Documentation

April 30th, 2008

For me, lightweight documentation is just another way of saying ‘good comments in code’. I do not want to have to create and maintain separate documentation for my systems, so I just make sure that every single page has relevant comments explaining what’s doing what.

The Advantages of Lightweight Documentation

At university, we were taught all about Heavy Documentation. ie. the formal documentation specifications, explaining every little detail of everything your system is doing. And guess what? Do you think those manuals we spent hours and hours on ever got used? Hell no. The next person on the project often ignores the documentation because it’s long-winded and boring. The end up calling you, because there were no simple lightweight explanations of things. Heavy documentation has it’s purposes, but I don’t believe it has a purpose in small-medium sized web development. Lightweight documentation through proper code commenting is the way forward.

Lightweight documentation is simple and to the point. It will explain what a chunk of code (or function/class) is doing, or what its purpose is. There’s nothing worse than coming into a project when the old developer has left and their code has zero comments, and YOU have to pick up where they’ve taken off. This has happened to me on numerous open source systems that I wished to modify for my own purposes. I just got lost in the sea of code because there were inadequate comments.

So What Things Are Important When Commenting?

You don’t have to get too formal about things, but since php functions don’t require types for parameters, a new programmer won’t necessarily know what types to pass for function parameters. And this is important information, as is information about what the function is actually doing.

For example

<?php
function get_result($var1, $var2){
   //do something
}
?>

Take the above function, and imagine it has hundreds of lines of code. As a new programmer to this project, I would not know anything about this function. It has no comments, and I have no idea if the parameters are integers, or strings, or other.

This is what your code could look like with lightweight documentation:

<?php
/*
* @summary: this function does calculations based on var1, and then compares the results to numbers found in var2
* @param: var1 is an integer
* @param: var2 is an array of floating point numbers, that we get from the time management system
*/
function get_result($var1, $var2){
   //do something
}
?>

Suddenly we can make sense of this function. Obviously if you are writing functions, you give them meaningful names, and meaningful parameter names. This function does not. But it serves the purpose of showing you the importance of lightweight commenting. Now I know what types var1 and var2 are, and we can start using this function, without having to spend 20 minutes scanning through the entire function figuring out what’s going on :-)

For You, The Programmer

Once you, the programmer, get into the habit of commenting, I think you’ll find it very beneficial for you, not just for future programmers who ‘might’ look at your code in the future. For example: If I’m thinking about a given function I’m going to write, I just start typing. All that information going through my head is useful stuff, and by writing it down, I can then review what I’ve written, find mistakes, fix them and improve my logic. So in the end I have great documentation and have taken a little extra time to make sure my function is both robust and performance friendly :-)

A Component Approach

April 30th, 2008

My preferred approach to any moderately sized system is a modular, or component based approach. I’ve had experience building medium-large systems, both completely on my own and with a team, and one thing I’ve learned is to not try and build a big system all at once. If you do, you’re bound to get stressed and confused (at least that’s what happens to me)

Instead, look at building lots of smaller, more manageable components, keeping things simple. If you break a big system up into smaller, more manageable pieces, your life will be a lot easier and you won’t get lost in the sea of code.

Advantages

The primary advantage of this approach is the ease of organising and planning your system, which I’ve found to always be the hardest part of any application. When you take a component based approach, everything is more easily manageable.

Another great advantage to the component based approach is that if you find performance to be poor in one component, you can easily fix that one component without it greatly affecting the rest of the system. If you keep your components loosely coupled, making changes to one component will not be difficult, so when planning your components, be smart about relationships between components. The more interlinked and depending things are on each other, the harder it is to make future upgrades as more and more things become affected by change.

Real world example

How did I approach this CMS? I got the basic backend framework organised. For me, it was just security and user management. Once I had security organised, and members organised, I had a basic login system. So those objects (security and members) were my first components. Then once those were in place, I just started to add components as I saw fit, step by step.

With this approach, before you know it that ‘basic framework’ will be holding lots of new components that you’ve added and you will suddenly have that big system you wanted, but without the stress ;-) Of course, the only trick is to make sure your basic framework is in good shape, otherwise all things that rely on those basic elements (like security and members) will suffer, especially if you make future changes to security because you didn’t think it through properly to begin with!

File Uploads

April 28th, 2008

This is a useful class I picked up somewhere, and possibly modified in the years since, that handles the work of file uploads. I’ve found it very useful, so hopefully you will to.

Firstly I’ll show you code to use this class. Create your form, taking careful note to include the enctype tag in the form, and MAX_FILE_SIZE field. In this case, the file field is called user_file. I also use a form1_submit field so I can easily catch this form submission.

<form name=”form1″ action=”" method=”post” enctype=”multipart/form-data” >
<input name=”form1_submit” id=”form1_submit” type=”hidden” value=”1″ />
<input type=”hidden” name=”MAX_FILE_SIZE” value=”1024000″ >
<input name=”user_file” id=”user_file” type=”file” class=”input” >
</form>

If the above form was included in our php file, we would catch form requests and process the upload as follows:

<?php
//message variable to tell us what has happened through this upload
$msg = '';
//catch form submission
if($_POST['form1_submit'] == '1') {
	//now upload the actual file
	$file_error = false;
	$upload_class = new Upload_Files;
	if($_FILES['user_file']['size'] > 0){
		$upload_class->temp_file_name = trim($_FILES['user_file']['tmp_name']);
		//prepare filename
		//I md5 the filename, so filenames can not be easily guessed by hackers should they somehow get past my folder security (have all your folders set to 755!)
		$fn1 = $_FILES['user_file']['name']; //Eg: filename = project1.doc
		//grab the file's extension (as i am md5ing the filename, I'll need the extension again)
		$extension = strtolower(strrchr($fn1,".")); //Eg: .doc
		//md5 the filename, adding back on the extension
		$fn1 = md5($fn1.time()).$extension;
		//set our class info
		$upload_class->file_name = $fn1; //Eg: filename will now be something like e343uwer0809809werwer.doc
		$upload_class->upload_dir = SITE_UPLOADS_DIR; //this is a constant of my site
		$upload_class->upload_log_dir = SITE_UPLOADS_DIR; //this is a constant of my site
		$upload_class->max_file_size = MAX_FILE_SIZE; //this is a constant of my site
		$upload_class->ext_array = $SITE_FILE_UPLOAD_EXTENSIONS; //this is a constant of my site
		//validation
		$valid_ext = $upload_class->validate_extension(); //class takes care of extension checking
		$valid_size = $upload_class->validate_size(); //class takes care of size checking
		$file_size = $upload_class->get_file_size(); //class tells me how big the uploaded file is
		$file_exists = $upload_class->existing_file(); //class tells me if this file already exists in my uploads folder
		//in rare case of same name, do this
		$max_rename = 0;
		while ($file_exists == true && $max_rename < 10) { //most times this will run is 10, so no chance of infinite
			$fn1 = md5($fn1.time()).$extension;
			$upload_class->file_name = $fn1;
			$file_exists = $upload_class->existing_file();
			$max_rename++;
		}
		//if still same name, remove existing
		if($file_exists == true) {
			unlink($directory_location.SITE_UPLOADS_DIR.$upload_class->file_name);
		}
		//start validation
		//I use PHP ftp commands to change my uplodas folder permissions to 777 while I upload the file, then set them back again to 755 when file has finished uploading (this allows my files to be publicly available (which i need) and stops me from leaving uploads folder open to hackers)
		//Check the necessary functions exist for what I'll be doing
		if (function_exists('ftp_connect') && function_exists('ftp_login') && function_exists('ftp_site') && function_exists('ftp_close')) {
			//open full write permissions for php file upload
			changemode(777, SITE_UPLOADS_DIR);
			//check validation status and upload file
			if ((int)$id == 0){
				$msg .= 'No document id found';
				$file_error = true;
			} elseif (!$valid_ext){
				$msg .= 'The file extension is invalid, please try again!';
				$file_error = true;
			} elseif (!$valid_size){
				$msg .= 'The file size is invalid, please try again!';
				$file_error = true;
			} else {
				$upload_file = $upload_class->upload_file_with_validation();
				if (!$upload_file){
					$msg .= 'Your file could not be uploaded!';
					$file_error = true;
				} else {
					$msg .= 'File successfully uploaded!';
					//insert our document
					//this is another class I have to track uploaded files, you can ignore
					$params = array(
						'id' => $id,
						'filename' => $upload_class->file_name,
						'filepath' => SITE_UPLOADS_DIR,
						'filesize' => $file_size,
						'filetype' => ''
					);
					$document_object = new Document();
					$document_object->db_update($params);
				}
			}
			//close off full write permissions for php file upload
			changemode(755, SITE_UPLOADS_DIR);
		} else {
			$msg .= 'To upload files, I require PHPs ftp_connect, ftp_login, ftp_site and ftp_close functions. One or more of these is not available.';
		}
	} else {
		$msg .= 'File was found to contain no data.';
	}
	//success or error ...what has happened?
	if($file_error == true){
		//report error
		echo $msg
	} else {
		//success
		echo $msg
	}
}
... HTML form would be shown down here somewhere probably
?>

So this is the class that the above PHP uses to process my file uploads. This does all the mundane work for me :-)

<?php
class Upload_Files
{
	//private variables
	var $temp_file_name;
	var $file_name;
	var $upload_dir;
	var $upload_log_dir;
	var $max_file_size;
	var $banned_array;
	var $ext_array;

	/**
	* Validate Extension
	* validate extensions passed to object
	*/
	function validate_extension() {
		//SECTION #1
		$file_name = trim($this->file_name);
		$extension = strtolower(strrchr($file_name,"."));
		$ext_array = $this->ext_array;
		$ext_count = count($ext_array);
		$valid_extension = '';

		if (!$file_name) {
			return false;
		} else {
			if (!$ext_array) {
				return true;
			} else {
				foreach ($ext_array as $value) {
					$first_char = substr($value,0,1);
						if ($first_char <> ".") {
							$extensions[] = ".".strtolower($value);
						} else {
							$extensions[] = strtolower($value);
						}
				}
				foreach ($extensions as $value) {
					if ($value == $extension) {
						$valid_extension = "TRUE";
					}
				}
				if ($valid_extension) {
					return true;
				} else {
					return false;
				}
			}
		}
	}

	/**
	* Validate Size
	* validate size based on parameter passed to object
	*/
	function validate_size() {
		$temp_file_name = trim($this->temp_file_name);
		$max_file_size = trim($this->max_file_size);

		if ($temp_file_name) {
			$size = filesize($temp_file_name);
				if ($size > $max_file_size) {
					return false;
				} else {
					return true;
				}
		} else {
			return false;
		}
	}

	/**
	* Existing File
	* checks if the file already exists in server location
	*/
	function existing_file() {
		$file_name = trim($this->file_name);
		$upload_dir = $this->get_upload_directory();
		if ($upload_dir == "ERROR") {
			return true;
		} else {
			$file = $upload_dir . $file_name;
			if (file_exists($file)) {
				return true;
			} else {
				return false;
			}
		}
	}

	/**
	* Get File Size
	* gets the size of the file and returns it with appropriate extension ...Meg, Gig, etc
	*/
	function get_file_size() {
		//SECTION #1
		$temp_file_name = trim($this->temp_file_name);
		$kb = 1024;
		$mb = 1024 * $kb;
		$gb = 1024 * $mb;
		$tb = 1024 * $gb;
		$file_size = '';

		if ($temp_file_name) {
			$size = filesize($temp_file_name);
			if ($size < $kb) {
				$file_size = "$size Bytes";
			} elseif ($size < $mb) {
				$final = round($size/$kb,2);
				$file_size = "$final KB";
			} elseif ($size < $gb) {
				$final = round($size/$mb,2);
				$file_size = "$final MB";
			} elseif($size < $tb) {
				$final = round($size/$gb,2);
				$file_size = "$final GB";
			} else 	{
				$final = round($size/$tb,2);
				$file_size = "$final TB";
			}
		} else {
			$file_size = "ERROR: NO FILE PASSED TO get_file_size()";
		}
		return $file_size;
	}

	/**
	* Get Max File Size
	* gets max file size based on parameter passed to object
	*/
	function get_max_size() {
		$max_file_size = trim($this->max_file_size);
		$kb = 1024;
		$mb = 1024 * $kb;
		$gb = 1024 * $mb;
		$tb = 1024 * $gb;
		$max_file_size = '';

		if ($max_file_size) {
			if ($max_file_size < $kb) {
				$max_file_size = "max_file_size Bytes";
			} elseif ($max_file_size < $mb) {
				$final = round($max_file_size/$kb,2);
				$max_file_size = "$final KB";
			} elseif ($max_file_size < $gb) {
				$final = round($max_file_size/$mb,2);
				$max_file_size = "$final MB";
			} elseif($max_file_size < $tb) {
				$final = round($max_file_size/$gb,2);
					$max_file_size = "$final GB";
			} else {
				$final = round($max_file_size/$tb,2);
				$max_file_size = "$final TB";
			}
		} else {
			$max_file_size = "ERROR: NO SIZE PARAMETER PASSED TO  get_max_size()";
		}
		return $max_file_size;
	}

	/**
	* Validate User
	* allows you to create a banned IP list so certain users can't upload ..stops trouble makers
	*/
	function validate_user() {
		//SECTION #1
		$banned_array = $this->banned_array;
		$ip = trim($_SERVER['REMOTE_ADDR']);
		$cpu = gethostbyaddr($ip);
		$count = count($banned_array);

		if ($count < 1) {
			return true;
		} else {
			foreach($banned_array as $key => $value) {
				if ($value == $ip ."-". $cpu) {
					return false;
				} else {
					return true;
				}
			}
		}
	}

	/**
	* Upload Directory
	* gets the set upload directory path
	*/
	function get_upload_directory() {
		$upload_dir = trim($this->upload_dir);

		if ($upload_dir) {
			$ud_len = strlen($upload_dir);
			$last_slash = substr($upload_dir,$ud_len-1,1);
			if ($last_slash <> "/") {
				$upload_dir = $upload_dir."/";
			} else {
				$upload_dir = $upload_dir;
			}
			$handle = @opendir($upload_dir);
			if ($handle) {
				$upload_dir = $upload_dir;
				closedir($handle);
			} else {
				$upload_dir = "ERROR";
			}
		} else {
			$upload_dir = "ERROR";
		}
		return $upload_dir;
	}

	/**
	* Log Directory
	* gets the log directory ...where we store logs about uploads to track what has been happening
	*/
	function get_upload_log_directory() {
		$upload_log_dir = trim($this->upload_log_dir);
		if ($upload_log_dir) {
			$ud_len = strlen($upload_log_dir);
			$last_slash = substr($upload_log_dir,$ud_len-1,1);
			if ($last_slash <> "/") {
				$upload_log_dir = $upload_log_dir."/";
			} else {
				$upload_log_dir = $upload_log_dir;
			}
			$handle = @opendir($upload_log_dir);
			if ($handle) {
				$upload_log_dir = $upload_log_dir;
				closedir($handle);
			} else {
				$upload_log_dir = "ERROR";
			}
		} else {
			$upload_log_dir = "ERROR";
		}
		return $upload_log_dir;
	}

	/**
	* Upload with NO validation
	* uploads a file with NO validation
	*/
	function upload_file_no_validation() {
		//SECTION #1
		$temp_file_name = trim($this->temp_file_name);
		$file_name = trim(strtolower($this->file_name));
		$upload_dir = $this->get_upload_directory();
		$upload_log_dir = $this->get_upload_log_directory();
		$file_size = $this->get_file_size();
		$ip = trim($_SERVER['REMOTE_ADDR']);
		$cpu = gethostbyaddr($ip);
		$m = date("m");
		$d = date("d");
		$y = date("Y");
		$date = date("m/d/Y");
		$time = date("h:i:s A");

		if (($upload_dir == "ERROR") OR ($upload_log_dir == "ERROR")) {
			return false;
		} else {
			if (is_uploaded_file($temp_file_name)) {
				if (move_uploaded_file($temp_file_name,$upload_dir . $file_name)) {
					$log = $upload_log_dir.$y."_".$m."_".$d.".txt";
					$fp = fopen($log,"a+");
					fwrite($fp,"$ip-$cpu | $file_name | $file_size | $date | $time");
					fclose($fp);
					return true;
				} else {
					return false;
				}
			} else {
				return false;
			}
		}
	}

	/**
	* Upload with validation
	* upload file with said validation
	*/
	function upload_file_with_validation() {
		//SECTION #1
		$temp_file_name = trim($this->temp_file_name);
		$file_name = trim(strtolower($this->file_name));
		$upload_dir = $this->get_upload_directory();
		$upload_log_dir = $this->get_upload_log_directory();
		$file_size = $this->get_file_size();
		$ip = trim($_SERVER['REMOTE_ADDR']);
		$cpu = gethostbyaddr($ip);
		$m = date("m");
		$d = date("d");
		$y = date("Y");
		$date = date("m/d/Y");
		$time = date("h:i:s A");
		$existing_file = $this->existing_file();    //<-Add On
		$valid_user = $this->validate_user();        //<-Add On
		$valid_size = $this->validate_size();        //<-Add On
		$valid_ext = $this->validate_extension();    //<-Add On

		if (($upload_dir == "ERROR") OR ($upload_log_dir == "ERROR")) {
			return false;
		} elseif ((((!$valid_user) OR (!$valid_size) OR (!$valid_ext) OR ($existing_file)))) {
			return false;
		} else {
			if (is_uploaded_file($temp_file_name)) {
				if (move_uploaded_file($temp_file_name,$upload_dir . $file_name)) {
					$log = $upload_log_dir.$y."_".$m."_".$d.".txt";
					$fp = fopen($log,"a+");
					fwrite($fp,"$ip-$cpu | $file_name | $file_size | $date | $time");
					fclose($fp);
					return true;
				} else {
					return false;
				}
			} else {
				return false;
			}
		}
	}
} //end class
?>

You may have noticed a function I used to change folder permissions on server …here it is:

<?php
function changemode($mod, $path)
{
   //call the required site constants
   global $FTP_HOST,$FTP_USERNAME,$FTP_PASSWORD,$FTP_ROOTDIR;
   // set up basic connection
   $conn_id = ftp_connect($FTP_HOST);

   // login with username and password
   $login_result = ftp_login($conn_id, $FTP_USERNAME, $FTP_PASSWORD);

   // try to chmod $path directory
   if (ftp_site($conn_id, 'CHMOD '.$mod.' '.$FTP_ROOTDIR.$path) !== false) {
       $success=TRUE;
   }
   else {
       $success=FALSE;
   }

   // close the connection
   ftp_close($conn_id);
	return $success;
}
?>

MySQL Database

April 28th, 2008

I try to use a central database class in my code, simply because if I ever need to migrate from MySQL to another database engine (such as Microsoft SQL or Postgres), all I have to do is alter the one database class file and my application is all converted. Since everything goes through the database class, it’s the only file you need to change in such an instance.

The class code is as follows …see comments for details:

<?php
# Mark's modified MySQL class (adapted from Sitepoint PHP books from Harry Fuecks - thanks Harry - awesome read and advise)
class MySQL {
	#standard connection vars
    var $host;
    var $dbUser;
    var $dbPass;
    var $dbName;

	#MySQL Resource link identifier stored here
    var $dbConn;

	#Stores error messages for connection errors
    var $connectError;

	#MySQL constructor
    function MySQL ($host,$dbUser,$dbPass,$dbName) {
        $this->host=$host;
        $this->dbUser=$dbUser;
        $this->dbPass=$dbPass;
        $this->dbName=$dbName;
        $this->connectToDb();
    }

	#Establishes connection to MySQL and selects a database
    function connectToDb () {
        // Make connection to MySQL server
        if (!$this->dbConn = @mysql_connect($this->host,
                                      $this->dbUser,
                                      $this->dbPass)) {
            trigger_error('Could not connect to server');
            $this->connectError=true;
        // Select database
        } else if ( !@mysql_select_db($this->dbName,$this->dbConn) ) {
            trigger_error('Could not select database');
            $this->connectError=true;
        }
    }

	#Checks for MySQL errors
    function isError () {
        if ( $this->connectError )
            return true;
        $error=mysql_error ($this->dbConn);
        if ( empty ($error) )
            return false;
        else
            return true;
    }

	#Returns an instance of MySQLResult to fetch rows with
	function query($sql) {
		global $DB_NUM_QUERIES;
		$DB_NUM_QUERIES++;
        if (!$queryResource=mysql_query($sql,$this->dbConn))
            trigger_error ('Query failed: '.mysql_error($this->dbConn).
                           ' SQL: '.$sql);
        return new MySQLResult($this,$queryResource);
    }
}

#MySQLResult Data Fetching Class
class MySQLResult {

	#Instance of MySQL providing database connection
    var $mysql;

	#Query resource
    var $query;

	#MySQLResult constructor
    function MySQLResult(& $mysql,$query) {
        $this->mysql=& $mysql;
        $this->query=$query;
    }

	#Fetches a row from the result
    function fetch () {
        if ( $row=mysql_fetch_array($this->query,MYSQL_ASSOC) ) {
            return $row;
        } else if ( $this->size() > 0 ) {
            mysql_data_seek($this->query,0);
            return false;
        } else {
            return false;
        }
    }

	#Fetches a row from the result
    function fetch_row () {
        if ( $row=mysql_fetch_row($this->query) ) {
            return $row;
        } else if ( $this->size() > 0 ) {
            mysql_data_seek($this->query,0);
            return false;
        } else {
            return false;
        }
    }

	#Returns the number of rows selected
    function size () {
        return mysql_num_rows($this->query);
    }

	#Returns the number of fields in table being queried
    function numFields () {
        return mysql_num_fields($this->query);
    }

	#Returns the field name from the query in the position you specify
    function FieldName ($field_number) {
        return mysql_field_name($this->query, $field_number);
    }

	#Returns the ID of the last row inserted
    function insertID () {
        return mysql_insert_id($this->mysql->dbConn);
    }
   
	#Returns the number of affected rows
    function affectedRows () {
        return mysql_affected_rows($this->mysql->dbConn);
    }

	#Checks for MySQL errors
    function isError () {
        return $this->mysql->isError();
    }
}
?>

Connect To Database

April 28th, 2008

I find it’s always useful to keep this functionality in a function. This example relies on some global variables …pretty easy to understand.

<?php
function connect_to_database(){
	global $DB_HOST,$DB_USERNAME,$DB_PASSWORD,$DB_DATABASE;
	$link = mysql_connect($DB_HOST, $DB_USERNAME, $DB_PASSWORD);
	if (!$link) {
	    die('Not connected : ' . mysql_error());
	}
	$db_selected = mysql_select_db($DB_DATABASE, $link);
	if (!$db_selected) {
	    die ('Can't find/use database : ' . mysql_error());
	}
}
?>