Oct 16th

PHP Class Autoloader Function

Anyone who has done serious work with PHP since 5.0 knows the way to stay future proof is to adopt and love like it was your own the object oriented programming model. This little snippet has gone a LONG way to making my life easier. To use it, simply include it in your globally included functions library (yes, functions not objects.. regular every day function methods).

In the snippet below there is a constant “PATH_CLASSES”, in my global definitions I have this defined as the absolute path to my class libraries which are all in the same directory for this purpose.

function __autoload( $class_name )
{
	$file_formats = array('%s.php', '%s.class.php', 'class.%s.php', '%s.inc.php');
	foreach ( $file_formats as $file_format )
	{
		$file = PATH_CLASSES.sprintf($file_format, strtolower($class_name));
		if ( file_exists($file) ) require_once($file);
	}
	return;
}
Oct 16th

PHP Search an array of objects for a property value

While developing a new project management application I needed a way to search an array of objects for a specific object property value. For example, I had an array of “employee” objects that contain a great many properties to describe each “employee” and you want to search this array for an employee object that has a specific value for one of it’s properties. To solve this I have come up with the following little snippet:

function search_object_array($needle_key, $needle_val, $haystack)
{
	// iterate through our haystack
	for ( $i = 0; $i < count($haystack); $i++ )
	{
		// ensure this array element is an object and has a key that matches our needle's key
		if ( is_object($haystack[$i]) and property_exists($haystack[$i], $needle_key) )
		{
			// determine if comparison is case sensitive
			if ( strtolower($needle_val) == strtolower($haystack[$i]->$needle_key) ) return $i;
		}
	}
	// no match found
	return false;
}

This function returns the index of the object array if a match is found and false if no matches are found.

So lets test it.
First I’ll define a class that we will use to generate our objects that will populate our array, well call this class “foo”.

class foo
{
	public $id;
	public $name;
	public $description;

	public function __construct( $id, $name, $description )
	{
		$this->id = $id;
		$this->name = $name;
		$this->description = $description;
	}
}

Next we’ll populate our object array “$obj_arr” with objects

$obj_arr[0] = new foo(1, 'Joe', 'Joes description');
$obj_arr[1] = new foo(2, 'Sara', 'Saras description');
$obj_arr[2] = new foo(3, 'Adam', 'Adams description');
$obj_arr[3] = new foo(4, 'Jake', 'Jakes description');
$obj_arr[4] = new foo(5, 'Sally', 'Sallys description');
$obj_arr[5] = new foo(6, 'Paul', 'Pauls description');
$obj_arr[6] = new foo(7, 'Freddie', 'Freddies description');

Now lets search our new array of objects for an object that matches our criteria:

$result = search_object_array('name', 'sally', $obj_arr);
echo $result;
// will print: 4

I hope this helps!

Oct 15th

MySQL Select the first and last ID from a table in a single select statement

Just a very quick little snippet that will allow you to SELECT the lowest and highest value from a single field in a database. For example, in a recent project I had to ensure that I was not deleting the first or last record status history record for a given project in a database table. To do this I came up with the following:

SELECT
	MIN(status_history_id) as first_id,
	MAX(status_history_id) as last_id
FROM table_status_history
WHERE ( project_id = "some_unique_id" )
GROUP BY project_id;

Essentially, this selects all status history records from the table for a specific project, then we group them all together and select only the max and min from the ID field and return those as (2) ad-hoc fields.

Oct 15th

JQuery UI Dialog not showing up twice in IE ( invalid argument )

I personally love the JQuery library, it makes life soo much easier. Add to that the JQuery UI libraries and you have yourself a seriously mean platform to build killer applications. That said, I ran into a mean little bug that affects the way the UI Dialog shows up on the page in Internet Exploder, I know… stop rolling your eyes it is here to stay and we will need to learn to play nice.

The bug:
Once a JQuery UI dialog has been initialized, opened once, and then closed it cannot be reopened in IE due to an “invalid argument” error.

The fix:
Open up your copy of jquery.js (you do have it locally right?, latency alert!) around line 1060 look for:

if ( set )
			elem[ name ] = value;

and change it to:

if ( set )
			try{elem[ name ] = value;} catch (error){};

Viola!

Sep 24th

PHP Recursive file search and replace function

I had a client recently who had unfortunately been attacked by one of those fun PHP insertion hacks, often associated with outdated installations of WordPress (fixed in later versions). Regardless it left the client with around 800 files with an identical block of code at the very beginning of the file.

I won’t get into treating the problem (the security weakness) here, instead I am just talking about how I treated the symptom, the malicious code that was inserted. I did a quick Google search and came up with a recursive directory/file listing function that suited my needs perfectly. Credit for this code is given in the below code.

NOTES:
It is very important to note that this code may not work for you perfectly out of the box as I created it for a specific task, for instance this script will ONLY search *.php and *.html files unless you add other extensions or remove that routine all together.

FUNCTION CODE:

/*
--------------------------------------------------------------------------------

	PHP SCRIPT
	---
	For:     Amyst Design Company
	By:      Doug Linsmeyer, Amyst Design Company
	Date:    2009-09-24
	Revised:
	Version: 0

--------------------------------------------------------------------------------
Purpose:
Recusively search all files and sub-folders for a regex pattern and
replace it with another strong and resave the file.

Some original code by mallsop.com on 05/2001.
Updated by mallsop.com on 04/2009.
PHP 5 or better. Tested ok on unix hosting box.
GPL License.
Some snippets from other php code posted online.
--------------------------------------------------------------------------------
*/

function scanDirectory($needle, $replacement, $dirid, $dirname, $path, $spaces)
{
	while ( ($file = readdir($dirid)) != false )
	{
		if ( ($file != '.') && ($file != '..') )
		{
			$dirname_full  = $dirname.'/'.$file;
			if ( is_dir($dirname_full) && ($file != 'cgi-bin') )
			{
				$return .= $spaces . '<b>' . $file . '</b><br />' . "\n";
				$dirid_next = opendir($dirname_full); // was just $file
				$newpath = $path.'/'.$file;
				$nextspaces = $spaces.'&nbsp;&nbsp;&nbsp;&nbsp;';
				$return .= scanDirectory($needle, $replacement, $dirid_next, $dirname_full, $newpath, $nextspaces);
				$nextspaces = '';
				closedir($dirid_next);
				$dirname_here = '';
			}
			else
			{
				$break = explode('.', $file);
				$file_ext = $break[1];
				$file_ext_lowercase = strtolower($file_ext);
				if ( ($file_ext_lowercase == 'php') OR ($file_ext_lowercase == 'html') )
				{

					$code = file_get_contents($dirname . '/' . $file);
					if ( preg_match($needle, $code) )
					{
						$matchtext = '<span style="color:#cc0000;">...Found.</span>';
						$newcode = preg_replace($needle, $replacement, $code);
						file_put_contents($dirname . '/' . $file, $newcode);
						$matchtext .= '...Fixed.';
					}
					else
					{
						$matchtext = '<span style="color:#00cc00;">...Not Found</span>';
					}
					$return .= $spaces . $file . $matchtext . '<br />' . "\n";

				}
			}
		}
	}
	return ($return);
}

IMPLEMENTATION:

$needle = '/\<\? [\/\*]{4}eval\(base64_decode\(\'[A-Za-z0-9]+\'\)\); \?\>/';
$replacement = '';
$dirname_start = '/home/wildchi2/public_html/';
$dirid_start = opendir($dirname_start);
$dirname_here = basename($dirname_start);
$spaces = '&nbsp;&nbsp;&nbsp;&nbsp;';
$output = scanDirectory($needle, $replacement, $dirid_start, $dirname_start, $dirname_here, $spaces);
?>
<html>
	<head>
		<title>Recursive File Search and Replace Utility by Amyst Design, 2009</title>
	</head>
	<body>
		<h1>Recursive Search and Replace Utility.</h1>
		<h2>By Amyst Design Co.</h2>
		<h3>Searching all files in the <?=$dirname_start?> folder.</h3>
		<?=$output?>
		<h3>Finished.</h3>
	</body>
</html>
Jun 18th

PHP Build Array of Years and Months Between Two Dates

On another recent project I had to generate monthly reports for the clients, to allow them to select the desired report I decided on giving them two HTML select inputs, the first for year and second for month. I tied the two fields together via ajax.

To populate these select boxes I wanted to build an multi-dimensional array of dates organized by year and then by month where the first date or month available is the date the client’s account was created (a date from the database) and the last date or month to be the current.

I build the following function to build my array of years and months, it is dependent upon another function posted on this site that builds an array of individual dates. These could be combined into one ‘SUPER FUNCTION’ but there was no need as the project required both functions discretely anyway.

The function requires two input options, a starting date and an ending date. These dates must be in the ‘0000-00-00′ format.

The result of:

$start = '2007-05-12';
$end = '2009-06-18';
print_r( year_month_array( $start, $end ) );

Would be:

Array
(
    [2007] => Array
        (
            [0] => May
            [1] => June
            [2] => July
            [3] => August
            [4] => September
            [5] => October
            [6] => November
            [7] => December
        )

    [2008] => Array
        (
            [0] => January
            [1] => February
            [2] => March
            [3] => April
            [4] => May
            [5] => June
            [6] => July
            [7] => August
            [8] => September
            [9] => October
            [10] => November
            [11] => December
        )

    [2009] => Array
        (
            [0] => January
            [1] => February
            [2] => March
            [3] => April
            [4] => May
            [5] => June
        )

)
function year_month_array($from, $to)
{
	$months = array('January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December');
	$dates = array_values(array_unique(get_date_range_array($from, $to, 'Y-n')));
	$data = array();
	foreach ( $dates as $date )
	{
		$date = explode('-', $date);
		$data[$date[0]][] = $months[$date[1]-1];
	}
	return $data;
}
Jun 18th

PHP Build Date Range Array

On a recent client project I was tasked with creating a chart of daily revenue from sales of, lets say, widgets. The first step to building this was to build an array of dates between two dates. I found a great little function from Mike’s Page, www.boonedocks.net that did almost exactly what I needed it to do. I need to use a few different date formats so I modified it slightly and I am re-posting it here for your use.

This function accepts (3) options, the first is the starting date which needs to be in the format ‘0000-00-00′. The second is the ending date which also needs to be in the format ‘0000-00-00′ and lastly the third is the out put date format, examples and ideas can be found in the PHP manual PHP: date – Manual.

My input dates were already in the ‘Y-m-d’ format so I didn’t bother changing this, but one could utilize the strtotime() function to make these two function input options variable.

The result is an array of dates in whichever format you define.

function get_date_range_array( $date_from, $date_to, $format = 'Y-m-d' )
{
	$date_array = array();
	$i_date_from = mktime(1, 0, 0, substr($date_from, 5, 2), substr($date_from, 8, 2),substr($date_from, 0, 4));
	$i_date_to = mktime(1, 0, 0, substr($date_to, 5, 2), substr($date_to, 8, 2),substr($date_to, 0, 4));
	if ( $i_date_to >= $i_date_from )
	{
		array_push($date_array, date($format, $i_date_from)); // first entry
		while ( $i_date_from < $i_date_to )
		{
			$i_date_from += 86400; // add 24 hours
			array_push($date_array, date($format, $i_date_from));
		}
	}
	return $date_array;
}
Jun 18th

PHP Random Password Function

Anyone who has ever dealt with user authentication knows how important a strong password is, this is a very simple, and small function that will generate a password of variable length and complexity. The function accepts two options; the first, length is self explanatory. This is the length of the password you wish to generate, a common length is 6-8. The second is the complexity of the password, this involves the possible characters from which the password will be generated. I designed this to accept values 1-4 (any other value will default to 4) where 1 is the weakest and 4 the strongest.

The function returns a generated password.

function generate_password ( $length = 8, $complexity = 3 )
{
	$password = '';
	switch ( $complexity )
	{
		case 1:
			$possible = '0123456789';
			break;
		case 2:
			$possible = '0123456789bcdfghjkmnpqrstvwxyz';
			break;
		case 3:
			$possible = '0123456789bcdfghjkmnpqrstvwxyzABCDEFGHIJKLMOPQRSTUVWXYZ';
			break;
		case 4:
		default:
			$possible = '0123456789bcdfghjkmnpqrstvwxyzABCDEFGHIJKLMOPQRSTUVWXYZ@#$%&!?^~';
			break;
	}
	$i = 0;
	while ( $i < $length )
	{
		// pick a random character from the possible ones
		$char = substr($possible, mt_rand(0, strlen($possible)-1), 1);
		// we don't want this character if it's already in the password
		if ( !strstr($password, $char) )
		{
			$password .= $char;
			$i++;
		}
	}
	return $password;
}
May 1st

PHP state and country list functions – JSON or Array

One of the most common select fields we are tasked to create as developers are state and country fields; most of us have a library of code snippets on hand for just such a thing, like a ready-made select list. On a recent project I was tasked with creating an autocomplete field and my select list just wouldn’t cut it as the JQUERY function I was using required JSON input.

To solve the problem I created a couple PHP functions that I use to build the lists dynamically, the functions output a state or country list as either a PHP array, or a JSON encoded array.

In the examples below I removed the line breaks in the arrays, simply because I prefer it to a very long list I have to scroll through.

Feel free to use these in your own projects. If you do, let me know.

Code:

function list_states ( $mode = 'JSON' )
{
	$states = array('AL'=>'Alabama', 'AK'=>'Alaska', 'AZ'=>'Arizona', 'AR'=>'Arkansas', 'CA'=>'California', 'CO'=>'Colorado', 'CT'=>'Connecticut', 'DE'=>'Delaware', 'DC'=>'District Of Columbia', 'FL'=>'Florida', 'GA'=>'Georgia', 'HI'=>'Hawaii', 'ID'=>'Idaho', 'IL'=>'Illinois', 'IN'=>'Indiana', 'IA'=>'Iowa', 'KS'=>'Kansas', 'KY'=>'Kentucky', 'LA'=>'Louisiana', 'ME'=>'Maine', 'MD'=>'Maryland', 'MA'=>'Massachusetts', 'MI'=>'Michigan', 'MN'=>'Minnesota', 'MS'=>'Mississippi', 'MO'=>'Missouri', 'MT'=>'Montana', 'NE'=>'Nebraska', 'NV'=>'Nevada', 'NH'=>'New Hampshire', 'NJ'=>'New Jersey', 'NM'=>'New Mexico', 'NY'=>'New York', 'NC'=>'North Carolina', 'ND'=>'North Dakota', 'OH'=>'Ohio', 'OK'=>'Oklahoma', 'OR'=>'Oregon', 'PA'=>'Pennsylvania', 'RI'=>'Rhode Island', 'SC'=>'South Carolina', 'SD'=>'South Dakota', 'TN'=>'Tennessee', 'TX'=>'Texas','UT'=>'Utah', 'VT'=>'Vermont','VA'=>'Virginia', 'WA'=>'Washington','WV'=>'West Virginia', 'WI'=>'Wisconsin','WY'=>'Wyoming');
	switch ( $mode )
	{
		case 'JSON':
			return json_encode($states);
			break;
		case 'Array':
			return $states;
			break;
	}
}

function list_countries ( $mode = 'JSON' )
{
	$countries = array( 'Afghanistan', 'Albania', 'Algeria', 'Andorra', 'Angola', 'Antigua and Barbuda', 'Argentina', 'Armenia', 'Australia', 'Austria', 'Azerbaijan', 'Bahamas', 'Bahrain', 'Bangladesh', 'Barbados', 'Belarus', 'Belgium', 'Belize', 'Benin', 'Bhutan', 'Bolivia', 'Bosnia and Herzegovina', 'Botswana', 'Brazil', 'Brunei', 'Bulgaria', 'Burkina Faso', 'Burundi', 'Cambodia', 'Cameroon', 'Canada', 'Cape Verde', 'Central African Republic', 'Chad', 'Chile', 'China', 'Colombi', 'Comoros', 'Congo (Brazzaville)', 'Congo', 'Costa Rica', 'Cote d\'Ivoire', 'Croatia', 'Cuba', 'Cyprus', 'Czech Republic', 'Denmark', 'Djibouti', 'Dominica', 'Dominican Republic', 'East Timor (Timor Timur)', 'Ecuador', 'Egypt', 'El Salvador', 'Equatorial Guinea', 'Eritrea', 'Estonia', 'Ethiopia', 'Fiji', 'Finland', 'France', 'Gabon', 'Gambia, The', 'Georgia', 'Germany', 'Ghana', 'Greece', 'Grenada', 'Guatemala', 'Guinea', 'Guinea-Bissau', 'Guyana', 'Haiti', 'Honduras', 'Hungary', 'Iceland', 'India', 'Indonesia', 'Iran', 'Iraq', 'Ireland', 'Israel', 'Italy', 'Jamaica', 'Japan', 'Jordan', 'Kazakhstan', 'Kenya', 'Kiribati', 'Korea, North', 'Korea, South', 'Kuwait', 'Kyrgyzstan', 'Laos', 'Latvia', 'Lebanon', 'Lesotho', 'Liberia', 'Libya', 'Liechtenstein', 'Lithuania', 'Luxembourg', 'Macedonia', 'Madagascar', 'Malawi', 'Malaysia', 'Maldives', 'Mali', 'Malta', 'Marshall Islands', 'Mauritania', 'Mauritius', 'Mexico', 'Micronesia', 'Moldova', 'Monaco', 'Mongolia', 'Morocco', 'Mozambique', 'Myanmar', 'Namibia', 'Nauru', 'Nepa', 'Netherlands', 'New Zealand', 'Nicaragua', 'Niger', 'Nigeria', 'Norway', 'Oman', 'Pakistan', 'Palau', 'Panama', 'Papua New Guinea', 'Paraguay', 'Peru', 'Philippines', 'Poland', 'Portugal', 'Qatar', 'Romania', 'Russia', 'Rwanda', 'Saint Kitts and Nevis', 'Saint Lucia', 'Saint Vincent', 'Samoa', 'San Marino', 'Sao Tome and Principe', 'Saudi Arabia', 'Senegal', 'Serbia and Montenegro', 'Seychelles', 'Sierra Leone', 'Singapore', 'Slovakia', 'Slovenia', 'Solomon Islands', 'Somalia', 'South Africa', 'Spain', 'Sri Lanka', 'Sudan', 'Suriname', 'Swaziland', 'Sweden', 'Switzerland', 'Syria', 'Taiwan', 'Tajikistan', 'Tanzania', 'Thailand', 'Togo', 'Tonga', 'Trinidad and Tobago', 'Tunisia', 'Turkey', 'Turkmenistan', 'Tuvalu', 'Uganda', 'Ukraine', 'United Arab Emirates', 'United Kingdom', 'United States', 'Uruguay', 'Uzbekistan', 'Vanuatu', 'Vatican City', 'Venezuela', 'Vietnam', 'Yemen', 'Zambia', 'Zimbabwe');
	switch ( $mode )
	{
		case 'JSON':
			return json_encode($countries );
			break;
		case 'Array':
			return $countries ;
			break;
	}

}
Apr 27th

SQLite Database Class

I have been wanting to try SQLite for some time and I recently got the chance to for a new project.

For the uninitiated:
SQLite is a flat-file database system with most of the functionality of larger database software like MySQL or MSSQL. SQLite is best suited for small footprint, light use databases, handling site newsreels or client contact data.

To start my new project I decided to clone a PHP database class that I have been using for some time for another platform. It does:

  • Database connection/creation.
  • Select, insert, and update queries.
  • Array based insert/update queries for rapid programming.
  • Error reporting on SQLite class functions.
  • Data scrubbing on insert/update queries to prevent issues.

Source Code:

class sqlite_database
{
	protected $link;
	protected $error;
	private $db_path = '/path/to/your/database.db';

	function __construct()
	{
		$this->link = sqlite_open( $this->db_path, 0666, $this->error ) or die ('Error: ' . $this->error);
	}

	function close()
	{
		sqlite_close($this->link);
	}

	function last_id()
	{
		return sqlite_last_insert_rowid($this->link);
	}

	function escape_str( $string )
	{
		return sqlite_escape_string($string);
	}

	function query( $sql )
	{
		$r = sqlite_unbuffered_query($this->link, $sql, SQLITE_ASSOC, $this->error) or die('Error: ' . $this->error . '<br />SQL: ' . $sql);
		if ( !$r )
		{
			return false;
		}
		else
		{
			if ( sqlite_valid($r) )
			{
				$data->dataset = array();
				while ( $row = sqlite_fetch_object($r) )
				{
					$data->dataset[] = $row;
				}
				$data->rows = count($data->dataset);
				return $data;
			}
			else
			{
				return false;
			}
		}
	}

	function query_insert( $table, $data )
	{
		$q = 'INSERT INTO ' . $table;
		$v = '';
		$n = '';
		foreach ( $data as $key => $val )
		{
			$n .= $key . ', ';
			if ( strtolower($val) == 'null' )
			{
				$v .= 'NULL, ';
			}
			elseif (strtolower($val) == 'now()' )
			{
				$v .= 'NOW(), ';
			}
			else
			{
				$v .= "'" . $this->escape_str($val) . "', ";
			}
    	}
		$q .= ' (' . rtrim($n, ', ') . ') VALUES (' . rtrim($v, ', ') . ');';
		$r = sqlite_unbuffered_query($this->link, $q, SQLITE_ASSOC, $this->error) or die('Error: ' . $this->error . '<br />SQL: ' . $q);
		return ( $r ) ? true : false;
	}

	function query_update($table, $data, $where)
	{
		$q = "UPDATE {$table} SET ";
		foreach ( $data as $key => $val )
		{
			if ( strtolower($val) == 'null' )
			{
				$q .= "`$key` = NULL, ";
			}
			elseif ( strtolower($val) == 'now()' )
			{
				$q .= "`$key` = NOW(), ";
			}
			else
			{
				$q .= "`$key`='" . $this->escape_str($val) . "', ";
			}
		}
		foreach ( $where as $key => $val )
		{
			$where_sql .= '( ' . $key[0] . ' ' . $key[1] . ' \'' . $key[2] . '\' ) ';
		}
		$where_sql = substr($where_sql, 0, -1);
		$q = rtrim($q, ', ') . ' WHERE ' . $where_sql . ';';
		$r = sqlite_unbuffered_query($this->link, $q, SQLITE_ASSOC, $this->error) or die('Error: ' . $this->error . '<br />SQL: ' . $q);
		return ( $r ) ? true : false;
	}
}

Examples
Connect to a database:

// require the sqlite database class library from Amyst Design
require('path/to/sqlite.php');
// instantiate the database object
// the path and file name of the database are set within the class
// If the database file doesn't exist SQLite will create the file
$db = new sqlite_database();

Insert a row of data:

// build the array of data to be inserted
// using key/value pairs, set keys to database field names.
$data = array('field1' => $value1,
			  'field2' => $value2,
			  'fieldn' => $valuen);
// assuming the database object has been instantiated
// function will return true or false depending on outcome
$result = $db->query_insert( 'tablename', $data );

Update a row of data:

// build the array of data to be inserted
// using key/value pairs, set keys to database field names.
$data = array('field1' => $value1,
			  'field2' => $value2,
			  'fieldn' => $valuen);
// build the array of criteria
// field name, operator, criteria
// this does not support complex where clauses
$where = array(array('fieldname1', '=', 'value1'),
			   array('fieldname2', '>', 'value2'));
// assuming the database object has been instantiated
// function will return true or false depending on outcome
$result = $db->query_insert( 'tablename', $data, $id_to_update );