Mark’s Site

Pensieve for coding and golf :-)

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());
	}
}
?>

Get Files From Folder

April 28th, 2008

Need to get a list of files from a folder on your server? Maybe it’s a list of photos that you want to manipulate with PHP, this function will grab all files from a specified directory and return them in an array for you. It uses recursive logic, calling itself to drop down all folders and subfolders.

<?php
//recursive function to create an array of files from a physical directory
function get_files($directory_to_scan) {
    //Try to open the directory
    if($dir = opendir($directory_to_scan)) {
        //Create an array for all files found
        $tmp = Array();
        //Add the files
        while($file = readdir($dir)) {
            //Make sure the file exists
            if($file != "." && $file != ".." && $file[0] != '.') {
                //If it's a directiry, list all files within it
                if(is_dir($directory_to_scan . "/" . $file)) {
                    $tmp2 = get_files($directory_to_scan . "/" . $file, "/" . $file);
                    if(is_array($tmp2)) {
                        $tmp = array_merge($tmp, $tmp2);
                    }
                } else {
                    array_push($tmp, "/" . $file);
                }
            }
        }
        //Finish off the function
        closedir($dir);
        return $tmp;
    }
}
?>

Encryption and Decryption

April 28th, 2008

Something I struggled to find when starting out was good, non standard encryption and decryption functions. Here’s a couple for you to use/adapt as you wish.

NOTE, these make use of a PHP constant called SALT. So make sure you define this first (see start of code for SALT)

<?php
#SALT (key used in encryption)
define("SALT", "you will never guess");

#encrypt a string
function encrypt($string) {
	$result = '';
	for($i = 0; $i < strlen($string); $i++) {
		$char = substr($string, $i, 1);
		$keychar = substr(SALT, ($i % strlen(SALT))-1, 1);
		$char = chr(ord($char)+ord($keychar));
		$result.=$char;
	}
	return base64_encode($result);
}

#decrypt an encrypted string
function decrypt($string) {
	$result = '';
	$string = base64_decode($string);
	for($i = 0; $i < strlen($string); $i++) {
		$char = substr($string, $i, 1);
		$keychar = substr(SALT, ($i % strlen(SALT))-1, 1);
		$char = chr(ord($char)-ord($keychar));
		$result.=$char;
	}
	return $result;
}
?>

PHP Disadvantages

April 28th, 2008

In light of all the advantages, you should also be aware of the problems with PHP.

It’s So Basic Everyone Has An Opinion

One problem I see with PHP is that it is so easy to learn and has such a large community, all willing to provide example code, that you often get some bad advice from people that have no software development background. This just perpetuates poor programming to others, which I don’t agree with.

Ultimate Flexibility

Because PHP is so flexible, it means you can develop things however you like. While this is great, to the inexperienced it means they can often get away with things they should NOT get away with. One great example was a setting in PHP4 that created variables on the fly from forms that were posted, or from query string data (register_globals was the configuration option). So instead of actually referencing the correct $_POST['my_field'] variable, users would just call $my_field. Variables would just seemingly be called from nowhere and you had no idea whether it was coming from POST or GET. This made code very hard to follow, and posed a massive security risk because a hacker could just manipulate your in-code variables by posting them to in the query string…

The obvious was bound to happen. When PHP5 came out, they turned this option off by default due to security concerns, and software broke worldwide. Administrators were rushing to the php.ini file to change the option back. But my point is, this option should always have been off by default, forcing programmers to create and call variables correctly.

Also, this flexibility extends to how you integrate PHP with HTML. We listed this as an advantage, but again, with inexperienced users, this can lead to very complicated code, for no reason. With .NET, you use a codebehind file to reference everything. Some people like this, some people don’t. But the point is you don’t have a choice. Which forces you to code in a certain way. For the inexperienced, this is a good thing, as it teaches them a certain way of programming, one that is pretty easy to grasp. With PHP however, you can choose to have a codebehind file that takes care of the processing, or do all your processing via what I call inline coding (everything in the one page), or a mixture of several codebehind files (which is the one I HATE). I’m seen PHP pages that send off form requests to a codebehind file, which sends that data off to another codebehind file, which then sends the information off to poorly named functions. WHAT THE HELL?! Why make things so complicated?

Anyway, you see my point. In the wrong hands, flexibility can be a bad thing :-)

Too Many Options

An ongoing debate with PHP versus .NET (which largely forces certain things on you, including a Visual Studio editor) is that PHP gives you too many choices for editors. You can choose to edit php files in Windows Notepad if you wish (not advised), or any one of hundreds of open source editors. Then you get commercial editors! What to choose?!?! Well the truth is, you need to spend a day or two trying out different editors, free and commercial (ones that come with free trials of course) and see what you prefer. For me, it’s NotePad++ all the way.

You should make a checklist of things that are important to your development.

For example: Some people use ‘find and replace’ a LOT, so this would be an important bit of functionality for your editor. So example questions for your analysis would be:

  • Does your editor have syntax highlighting?
  • Do you WANT syntax highlighting? Some people hate it, some people (like me) love it. How fast is you editor at working with multiple files?
  • CAN it work with multiple files?
  • Does it have tab support for multiple files?
  • Does it have an option to save all files that you are working on?
  • Does it have an option to group code into projects that you can save?
  • etc. etc.

That is how your choose a development tool in PHP. Just be smart about it and the sea of options is actually a blessing.

PHP Advantages

April 28th, 2008

If you’re beginning your journey with web development, it helps to understand why you would choose PHP over another language. You could of course make the wrong decision like Mac users have with their entire operating system (unless your Mac has Windows XP on it - thank you Intel!). You want to consider all of your options, not just the prettiest hip model. I’ve had experience with java, javascript, PHP and .NET, so hopefully I can pass onto you some of the reasons why I choose PHP over all others when given the opportunity.

Easy to follow language

PHP is a very easy language to read and understand. I was taught Java at university, and once my eyes read some PHP code for the first time I could very easily read it, all of it. I’ve heard C++ coders say the same thing.

Pseudo code example:

if(some condition) {
   do this;
   and this;
} else {
   do that;
}

If you can get your head around that simple syntax, PHP will be an easy language for you to pickup. If you find that logic difficult to comprehend, you better get used to paying someone else to do your programming. Your time will be better spent elsewhere.

That said, a lot of other languages are just as easy to read, including .NET and java, so I am merely pointing out that PHP is easy to read, not that it is easier to read than any other language (except C and Ruby - to me they are not programming languages, but some foreign dialect that only the mentally ill understand). So please, save me your hate mail (that includes you Mac users). I do have other reasons for choosing PHP …

So we can safely assume if you’re still reading, you will find PHP simple to read. Even when complex methods of programming are used, you can still quite easily follow things from place to place, even if it takes you some time with complex object oriented code. After all, big complex problems are often just many smaller problems all combined and/or working together. The sooner you realise this, the easier it will be to handle larger projects with PHP.

The Language is Free (and so are great tools for development)

Whether your development is commercial or personal, you can develop with PHP for free. You can opt to use a commercial based editor that will cost money (such as Zend development tools), but I use Notepad++ (which is open source) for all of my personal PHP development (this very site is one example built entirely from Notepad++ - with my fine ass logo designed with the Gimp image editor, also open source). I find Notepad++ gives me ultimate flexibility, and all the options I want for small and medium projects (I haven’t worked on any large projects).

I believe Notepad++ is just plain better than Zend. Why? It loads faster, has better find and replace functionality and can work with multiple files more easily. For example, ‘find and replace’ in Zend is horrible. The version of Zend I’m using at work does ‘find and replace’ only in the one direction you specify from where your cursor is, and you cannot choose to ‘find and replace’ across all open files. WTF! Find and replace is a programmers best friend, and Zend just doesn’t have it right yet. Notepad++ however, does ‘find and replace’ perfectly. Even allowing regular expression replacements, which is awesome. You’d think a commercial editor that you’re paying for would have all the bells and whistles, but the fact is that’s not true. You need to take some software trials, do your research and find one that works for you, and a free editor may be your ultimate preference. So you have my recommendation. Notepad++ is the way to go with PHP.

Choice of editors, to me, is a huge advantage for PHP, as .NET only has one main development tool, Visual Studio, which you have to pay thousands for, PER development machine. (And for all you .NET freaks who say there are open source alternatives to Visual Studio, YES I KNOW, I have tried them and they really do not compare to Visual Studio, sorry. One came close, i can’t remember its name, but I used it for a month and it just didn’t measure up to Vis Studio). If you choose to use Microsoft for development, get used to paying for everything, per development machine :-) Another plus for PHP.

Compiled ‘On The Fly’

Probably my most favourite reason for using PHP is the fact that it is compiled at run time, meaning you, the developer, do not have to compile PHP code whenever you change something. I also work with VB.NET, and within an hour of development in .NET I’m sick of compiling. Being forced to compile code into DLLs (dynamic link library files) is a pain in the hole, especially once you are used to the ‘on the fly’ world of PHP. Why is compiling so bad? There’s the compile time itself right …let’s wait 5-20 seconds for our application code to compile - and that’s EVERYTIME you want to change something and see the change. But that’s not all, now that you’ve compiled, your application’s DLL now has a new version number, forcing IIS and .NET to shit themselves and take 10-20 seconds to make you log back in to your application because your DLL is a new version. Ahhhhhhhhhh. So much more wasted development time, especially when you add up all that wasted time over the course of a year or more. It’s like the years we spend driving our cars over the course of our lifetime. At least while driving you get to take in the scenes, punch the excelerator and possibly enjoy the ride. With compiling time, you’re usually staring at the monitor yelling at the top of your lungs: “HURRY THE FUCK UP”.

So you’ve made the right choice if you have chosen PHP :-)

Great Free Community Support

Can’t figure out something in your code? Google it and you’ll have hundreds of pages all openly giving advice. This is mostly true for most languages, but not all. PHP has been in use for a very long time and has a massive community. Basically, as long as you know how to search correctly and/or describe your problem, someone, somewhere will have had your problem before and will have an answer online for you. Just make sure, before asking at forums, that you have SEARCHED the forums first, otherwise you’ll get hate responses saying “Search the fucking forum moron, we’ve dealt with your problem countless times”. Which does nothing for your self esteem if you are a programmer starting out …but I guess it’ll teach you the rules of forums quick smart :-)

If you can’t find the answer online from free information, experts-exchange.com is a great place to go for really hard, possibly unique problems. You’ll get quick responses to your problems from experts all over the world. (Free and paid options available for this type of support)

The community also has a LOT of free code available for you to use. You can often find examples or complete tutorials describing how to do different things in PHP. My site is one such example :-)

Integrates Easily With HTML

Some languages make it quite difficult to integrate code with the user interface (.NET uses ASP.NET with controls in amongst HTML, which I find annoying because it’s just something else to learn on top of HTML) but with PHP, if you know HTML, you’re all set.

For example:

<?php
$my_number = 21 + 5;
?>



this is some text and the calculation's value is <?=$my_number?>

Those are my favourite reasons for using PHP. I’m sure there are many others.

New Workout Schedule

April 28th, 2008

I’m using my blog for this so I always have a reference. All sets consist of 8.

Monday Tuesday Wednesday Thursday Friday
Back and Biceps

  • 3 sets of wide-grip pulldowns
  • 3 sets of one arm dumbbell rows
  • 3 sets of standing dumbbell curls
  • 2 sets of dumbbell curl 21s

Cardio (mini suicides)

Chest and Triceps

  • 4 sets of bench press
  • 2 sets of bench press 21s
  • 3 sets of lying tricep extensions
  • 2 sets of standing tricep presses
  • 2 sets of dumbbell kickbacks

Cardio (mini suicides)

Shoulders

  • 3 sets of military presses
  • 3 sets of standing lateral raises
  • 3 sets of front dumbbell raises

Abs (crumches and twisties)

Back and Biceps
Cardio (mini suicides)
Chest and Triceps
Cardio (mini suicides)

Database permissions

April 27th, 2008

Real world example

This site has public content (what you are reading now) and secure content (pages that control what you are reading now, among other things).

For my public site, I have a MySQL user setup who only has SELECT privileges. This way, if someone does manage to find a way around my class security and tries some SQL injection code, the worst they can do is SELECT (no harm to my database).

In the secure section however, I open up permissions by using a different mysql user, one who has SELECT/INSERT/UPDATE and DELETE permissions (S/I/U/D). This allows them everything they need to manage their content. So it is safe to say I trust users that I let into my administration area because I don’t want someone who fancies themselves as a hacker into an area where I have writable permissions (such as INSERT and DELETE).

Why are writable permissions bad?

The INSERT permission is problematic, because a hacker could use it to flood my database and possibly crash my server.

The UPDATE permission is problematic because a hacker could use it to modify existing data, leading to data corruption.

The DELETE permissions is problematic, because, well, use your imagination :-)

So you can see why you need to be careful about what permissions you allow on your public site which is accessible to anyone online. I know many applications that accept public user input and just have a single MySQL user setup with S/I/U/D permissions. This is really a hacker’s dream I imagine.

And for goodness sakes people, make sure your data is slashed before going into your SQL statement (if you don’t use prepared statements that is). Just today I added a comment into a very popular website and had it return a SQL error to my eyes because I had a single quote character in my comment. It broke the INSERT statement and showed me that if I wished, I could very easily inject SQL code into their database. The warning told me two important pieces of data:

  1. that their system did not add slashes to unsafe characters, so the site was vulnerable to simple SQL injection techniques
  2. the name of the comment table (because it showed me the broken SQL statement), which I could then write a series of INSERT statement to flood their system

Luckily I’m not a hacker :-)

My new CPU, but not Quad

April 25th, 2008

It was a very hard decision, but I couldn’t justify the extra $$ for a quad core. I took a long hard look at the applications that I use (development applications like Visual Studio, SQL Server, Zend, and so on), but in the end I would only have been purchasing a quad core for Far Cry 2, which is coming out late this year and supposedly takes full advantage of multiple cores (demos were done with 2 quad cores, yes 8 cores!). And that really wasn’t a good enough reason for me to go quad core, considering there is no hard proof yet that Far Cry 2 will in fact run a lot better on quad core versus dual cores.

I did however, purchase a new CPU today, as I was noticing lag on my e4300 in the everyday tasks that I do. Something had to be done. The slowness could not stand! So I did LOADS of research into core 2 quads and standard core 2 duos (over a week in fact at a couple of hours a night), and made my choice for a processor that I could overclock to high heaven, and, if I had any troubles with overclocking, would still be happy with both the price and processor stock speed. The answer in the end was the e8200. Stock speed of 2.66Ghz, and a whole page dedicated to how easy the CPU is to overclock (http://www.utheguru.com/overclocking-an-intel-e8200-core-2-duo-processor-2M), I just couldn’t resist.

So I saved $150 as I was looking very closely at the q9300 (quad core), but I just couldn’t see the use of the additional cores for the cost. Sure, in a year or two, more applications will be released that will GREATLY take advantage of quad core, but by then DDR2 will be something to laugh about and Front Side Buses of 1333 will be a joke also, as I’m sure everything will have tripled in power by then, so I’ll be upgrading my whole computer I’m sure in another 2-3 years.

For now, with the current state of the application and games industry. I believe a good core 2 duo is still the way to go. Vista is blisteringly fast with this new CPU running at 3.6Ghz. (Yes, I got a stable, happy temperature overclock from 2.66 to 3.6Ghz - HOW INSANE IS THAT!?)

Here’s my CPU frequency settings from CPUZ (note, my e4300 at stock got a 1M reading of 31 seconds, and my 3Ghz P4 got 1M in just over 2 minutes, so 12 seconds is brilliant for this new 45nm breed processor):

And here’s my memory frequency readings from CPUZ (note: that’s standard Geil DDR800):

Here’s temp readings from Coretemp:

And I should also mention my SuperPI readings …of 12-13 seconds over 1M!!! :-)))

Here’s my SuperPI output:

So if, like me, you are mildly confident about overclocking, you can get a base version for around $200, and overclock it to speeds of $400+ models (you’ll need a non-stock cooler though, and a motherboard which allows overclocking, which you need to know all about before deciding anything).

I must say I got lucky when I purchased a new motherboard for our HTTP server a few months ago. It was better than my existing board! So I did the ol’ switch this weekend and put my old motherboard (which was still a very good board: Gigabyte 965PDS3 rev 3.3 solid capacitor, 1333FSB etc etc.) in favour of the new Gigabyte P35-DS3, which has a higher FSB, the latest and greatest of everything, and support for quad cores and the new 45nm Intel processors (which the old board did not have, unless you used a beta BIOS version which I wasn’t too happy about trying).

So yea, a good weekend, as I got a newer, more powerful motherboard, a new cpu which I overclocked EASILY and I even managed to overclock my 800 rated ram to 900, and all is running brilliantly! My first ever overclock where I haven’t had issues. I’m happily surprised. Thanks Intel, and a special thanks to the above mentioned article on e8200 processors, which without this I would have spent an extra $150 on a q9300, for (I believe) nothing! :-)