PHP Function Library

So I’ve been adding a bunch of function pages lately, and decided it’d be easier if I just included the little library I’ve been working on for work. When making these I kept in mind that we’d want to share/use in other places so I made sure they don’t have passwords or API keys in them, and that they don’t reference specific systems that would be specific to our environment.

class.validation.php is completely independent. functions.utility.php currently requires class.http.php but I am working on removing the requirement.

functions.utility.php

<?php

/**
 * Signup Framework Utility Library
 *
 * This package contains functions used in manipulating data.
 *
 * @package signupFramework
 * @subpackage utility
 */

/**
 * Start session
 *
 * @author Jacob Allred <jacob@jacoballred.com>
 * @param int $expireTime
 * @return boolean
 */
function utility_StartSession($expireTime = 604800){
	if(!is_numeric($expireTime)){ return false; }
	session_set_cookie_params($expireTime);
	session_start();
	ini_set('session.cache_limiter', 'private');
	return true;
}

/**
 * Do the reverse of htmlspecialchars()
 *
 * @author Jacob Allred <jacob@jacoballred.com>
 * @param string $string
 * @return string
 */
function utility_unhtmlspecialchars($string){
	$string = str_replace('&amp;',	'&',	$string);
	$string = str_replace('&#039;',	'\'',	$string);
	$string = str_replace('&quot;',	'"',	$string);
	$string = str_replace('&lt;',	'<',	$string);
	$string = str_replace('&gt;',	'>',	$string);
	return $string;
}

/**
 * Clean an address using the USPS address validation web service.
 *
 * Must have at least address2 and (city/state or zip5).
 *
 * Please note that address2 is the street address (IE, 100 Oak St), and
 * address1 is the secondary location information (IE, Apt 12).
 *
 * @author Jacob Allred <jacob@jacoballred.com>
 * @param array $address
 * @param array $username Username provided by USPS
 * @return array
 */
function utility_ValidateAddress($address, $username){
	// Check for minimum parts
	if(!is_array($address)){ $parsed['error']['number'] = 101;$parsed['error']['description'] = 'Address isn\'t an array.'; return $parsed; }
	if(!isset($address['address2'])){ $parsed['error']['number'] = 102;$parsed['error']['description'] = 'Address2 is missing.';return $parsed; }
	if(!(isset($address['city']) && isset($address['state'])) && !isset($address['zip5'])){
		$parsed['error']['number'] = 103;$parsed['error']['description'] = 'Address must have city/state or zip5.';return $parsed;
	}

	// Make sure arguments are valid
	//if(isset($address['firmname']) && strlen($address['firmname']) > 38){
	//	$address['firmname'] = substr($address['firmname'],0,38);
	//}
	if(isset($address['address1']) && strlen($address['address1']) > 38){
		$address['address1'] = substr($address['address1'],0,38);
	}
	if(strlen($address['address2']) > 38 || strlen($address['address2']) < 1){
		$parsed['error']['number'] = 104;$parsed['error']['description'] = 'Address2 must be 1 to 38 characters.';return $parsed;
	}
	if(isset($address['city']) && strlen($address['city']) > 15){
		$address['city'] = substr($address['city'],0,15);
	}
	if(isset($address['state']) && strlen($address['state']) != 2){
		$parsed['error']['number'] = 105;$parsed['error']['description'] = 'State must be 2 character abbreviation.';return $parsed;
	}
	if(isset($address['zip5']) && (strlen($address['zip5']) != 5 || !is_numeric($address['zip5']))){
		$parsed['error']['number'] = 106;$parsed['error']['description'] = 'Zip5 must be 5 numeric characters.';return $parsed;
	}
	if(isset($address['zip4']) && (strlen($address['zip4']) != 4 || !is_numeric($address['zip4']))){
		$parsed['error']['number'] = 107;$parsed['error']['description'] = 'Zip4 must be 4 numeric characters.';return $parsed;
	}

	$xmlstr = '<AddressValidateRequest USERID="'.$username.'"><Address ID="0"><FirmName></FirmName><Address1></Address1><Address2></Address2><City></City><State></State><Zip5></Zip5><Zip4></Zip4></Address></AddressValidateRequest>';

	$xml = new SimpleXMLElement($xmlstr);
	require_once('class.http.php');
	$h = new http();

	// Populate Address
	$xml->Address->Address1 = isset($address['address1']) ? $address['address1'] : '';
	$xml->Address->Address2 = $address['address2'];
	$xml->Address->City = isset($address['city']) ? $address['city'] : '';
	$xml->Address->State = isset($address['state']) ? $address['state'] : '';
	$xml->Address->Zip5 = isset($address['zip5']) ? $address['zip5'] : '';
	$xml->Address->Zip4 = isset($address['zip4']) ? $address['zip4'] : '';

	// Prepare for XML call
	$h->xmlrequest = $xml->asXML();

	$output = str_replace(array("\n",'<?xml version="1.0"?>',"\t",),array('','',''),$xml->asXML());
	$url = 'http://Production.ShippingAPIs.com/ShippingAPI.dll?API=Verify&XML='.urlencode($output);

	// Submit data and get results
	$h->fetch($url,0,'','','','GET');

	// Put results into XML object
	$xml = new SimpleXMLElement($h->body);
	unset($h); // Cleanup

	// Check if XML had errors
	if(isset($xml->Address->Error)){
		$address['error']['description']	= strval($xml->Address->Error->Description);
		$address['error']['number']			= 201;
		return $address;
	}

	if(isset($xml->Address->ReturnText)){
		$address['error']['description']	= strval($xml->Address->ReturnText);
		$address['error']['number']			= 200;
	}

	// Pull apart XML
	$address = array();
	//$address['firmname']	= isset($xml->Address->FirmName)	? strval($xml->Address->FirmName)	: false;
	$address['address2']	= isset($xml->Address->Address2)	? strval($xml->Address->Address2)	: false;
	$address['address1']	= isset($xml->Address->Address1)	? strval($xml->Address->Address1)	: false;
	$address['city']		= isset($xml->Address->City)		? strval($xml->Address->City)		: false;
	$address['state']		= isset($xml->Address->State)		? strval($xml->Address->State)		: false;
	$address['zip5']		= isset($xml->Address->Zip5)		? strval($xml->Address->Zip5)		: false;
	$address['zip4']		= isset($xml->Address->Zip4)		? strval($xml->Address->Zip4)		: false;

	unset($xml); // Cleanup

	return $address;
}

/**
 * Detect whether the user is using HTTPS.
 * This function MAY NOT operate as expected if the request is being proxied.
 *
 * @author Jacob Allred <jacob@jacoballred.com>
 * @return boolean Returns TRUE if connection is made using HTTPS
 */
function utility_DetectHTTPS(){
	if(isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on'){
		return true;
	}else{
		return false;
	}
}

/**
 * Return a string formatted like $5.90
 *
 * @author Jacob Allred <jacob@jacoballred.com>
 * @param string $number
 * @return string
 */
function utility_FormatDollars($number){
	if(is_numeric($number) == false){
		return false;
	}
	if(strpos($number, '.') !== false){
		$temp = explode('.',$number);
		$temp[1] = sprintf("%-02s",$temp[1]);
		$number = $temp[0].'.'.$temp[1];
	}else{
		$number = number_format($number,2);
	}
	return $number;
}

/**
 * Return reported browser agent
 *
 * @author Jacob Allred <zulugrid@gmail.com>
 * @return string
 */
function utility_HTTPUserAgent(){
	$agent = strtolower($_SERVER['HTTP_USER_AGENT']);

	if(stristr($agent,'msie')){
		return 'msie';
	}elseif(stristr($agent,'firefox')){
		return 'firefox';
	}elseif(stristr($agent,'safari')){
		return 'safari';
	}elseif(stristr($agent,'opera')){
		return 'opera';
	}else{
		return 'unknown';
	}
}

/**
 * Return an escaped string.
 *
 * @author Jacob Allred <jacob@jacoballred.com>
 * @param string $string
 * @param string $type
 * @return string
 */
function utility_Escape($string, $type = 'mysql'){
	switch(strtolower($type)){
		case 'mysql':
			// Add slashes
			if(get_magic_quotes_gpc()){
				$string = stripslashes($string);
			}
			return mysql_escape_string($string);
			break;
		case 'mssql':
			// Replace single quotes with single quotes X2
			return str_replace("'","''",$string);
			break;
		case 'js':
		case 'javascript':
			// Add slashes so single quotes can be used inside of single quotes
			return mysql_escape_string($string);
			break;
		default:
			// Unknown escape type
			return false;
			break;
	}
}

/**
 * Return a string that has been properly cased.
 * Only changes letters to lowercase if there is a space in the string.
 * This prevents things like "NPR" from getting converted to "Npr".
 *
 * @author Jacob Allred <jacob@jacoballred.com>
 * @param string $string
 * @param boolean $onyIfUpper Only change string if it is in all caps already
 * @return string
 */
function utility_ProperCase($string, $onlyIfUpper = true){
	if($onlyIfUpper == true && $string != strtoupper($string)){
		return $string;
	}

	// Start with lowercase string if requested
	if(strpos($string,' ') !== false){
		$string = strtolower($string);
	}

	// Use PHP's uppercase words function to get started
	$string = ucwords($string);

	// Fix McWhatever
	$stringSlash = -1;
	while(strpos($string,'Mc',$stringSlash+1) !== false){
		$stringSlash = strpos($string,'Mc',$stringSlash+1);
		$string = substr_replace($string,strtoupper(substr($string,$stringSlash+2,1)),$stringSlash+2,1);
	}

	// Fix (Whatever)
	$stringSlash = -1;
	while(strpos($string,'(',$stringSlash+1) !== false){
		$stringSlash = strpos($string,'(',$stringSlash+1);
		$string = substr_replace($string,strtoupper(substr($string,$stringSlash+1,1)),$stringSlash+1,1);
	}

	// Fix /Whatever
	$stringSlash = -1;
	while(strpos($string,'/',$stringSlash+1) !== false){
		$stringSlash = strpos($string,'/',$stringSlash+1);
		$string = substr_replace($string,strtoupper(substr($string,$stringSlash+1,1)),$stringSlash+1,1);
	}

	// Fix abbreviations (A.m.e. should be A.M.E.)
	$stringSlash = -1;
	while(strpos($string,'.',$stringSlash+1) !== false){
		$stringSlash = strpos($string,'.',$stringSlash+1);
		$string = substr_replace($string,strtoupper(substr($string,$stringSlash+1,1)),$stringSlash+1,1);
	}

	// Fix specific abbreviation issues
	// Add to array if a common abbreviation is found in your data that needs fixed
	$abbreviations = array(
		'Ame' => 'AME'
	);
	foreach($abbreviations as $bad => $good){
		$string = preg_replace(array('/ '.$bad.'$/','/ '.$bad.' /','/^'.$bad.' /'),array(" $good"," $good ","$good "),$string);
	}

	return $string;
}

/**
 * Format phone number
 *
 * @author Jacob Allred <jacob@jacoballred.com>
 * @param string|int $phone Telephone number to be formatted
 * @param int $style Formatting preference
 * @return string
 */
function utility_FormatPhoneNumber($phone, $style = 1){
	$phone = preg_replace('/[^0-9]/','',$phone);

	if(substr($phone,0,1) == 1){
		$phone = substr($phone,1);
	}

		if (strlen($phone) == 7){
				sscanf($phone, "%3s%4s", $prefix, $exchange);
		}elseif(strlen($phone) == 10){
				sscanf($phone, "%3s%3s%4s", $area, $prefix, $exchange);
		}elseif(strlen($phone) > 10){
				sscanf($phone, "%3s%3s%4s%s", $area, $prefix, $exchange, $extension);
		}else{
				return $phone;
		}

	if($style == 1){
			$out = "";
			$out .= isset($area) ? $area . '-' : "";
			$out .= $prefix . '-' . $exchange;
		$out .= isset($extension) ? ' x' . $extension : "";
	}else{
				$out = "";
				$out .= isset($area) ? '(' . $area . ') ' : "";
				$out .= $prefix . '-' . $exchange;
				$out .= isset($extension) ? ' x' . $extension : "";
	}
		return $out;
}

/**
 * Attempt to determine user's IP address
 *
 * @author Jacob Allred <jacob@jacoballred.com>
 * @return string
 */
function utility_GetIP(){
	// Determine customer's IP address
	if(isset($_SERVER['HTTP_X_FORWARDED_FOR'])){
		// They are using a proxy, so we grab the forwarded for IP
		$theirIP = $_SERVER['HTTP_X_FORWARDED_FOR'];
	}elseif(isset($_SERVER['REMOTE_ADDR'])){
		// No proxy, grab the normal IP
		$theirIP = $_SERVER['REMOTE_ADDR'];
	}else{
		// No IP (unlikely).
		$theirIP = false;
	}
	return $theirIP;
}

?>

class.validation.php

<?php

/**
 * Signup Framework Validation Library
 *
 * This package contains validation functions and cleaning functions.
 *
 * If you add a function to this file, then you MUST add a matching function to
 * functions.validation.js. Matching function MUST have the same name, take the
 * same input parameters, and return (as closely as possible) the same results
 * as the function in this file.
 *
 * @package signupFramework
 * @subpackage validation
 */

/**
 * This class holds all of our handy validation functions
 *
 * @author Jacob Allred <jacob@jacoballred.com>
 */
class validate{

	/**
	 * Validate and clean a North America telephone number.
	 * Returns cleaned number or false if the number is invalid.
	 *
	 * @author Jacob Allred <jacob@jacoballred.com>
	 * @param string $phone
	 * @return mixed
	 */
	public function PhoneNumber($phone){
		// Remove non-numeric characters
		$phone = preg_replace('/[^0-9]/','',$phone);

		// Remove first character if it is a 1 (country code)
		if($phone[0] == '1'){
			$phone = substr($phone,1);
		}

		// Verify length and return value
		if(strlen($phone) == 10){
			return $phone;
		}else{
			return false;
		}
	}

	/**
	 * Validate and clean a US zip code.
	 * Returns cleaned zip code or false if invalid.
	 *
	 * @author Jacob Allred <jacob@jacoballred.com>
	 * @param string $zip
	 * @param int $returnLength
	 * @return mixed
	 */
	public function ZipCodeUS($zip, $returnLength = 9){
		// Remove non-numeric characters
		$zip = preg_replace('/[^0-9]/','',$zip);

		// Verify length
		if(strlen($zip) != 5 && strlen($zip) != 9){
			return false;
		}else{
			// Truncate to ZIP5 if needed, otherwise return full zip
			if($returnLength == 5){
				return substr($zip,0,5);
			}else{
				return $zip;
			}
		}
	}

	/**
	 * Validate and clean a CA zip code.
	 * Returns cleaned zip code or false if invalid.
	 *
	 * @author Jacob Allred <jacob@jacoballred.com>
	 * @param string $zip
	 * @param int $returnLength
	 * @return mixed
	 */
	public function ZipCodeCA($zip){
		// Remove non-alphanumeric characters
		$zip = $this->ReturnAlphanumeric($zip);

		// Verify length
		if(strlen($zip) != 6){
			return false;
		}

		// Force uppercase
		$zip = strtoupper($zip);

		// Verify zip follows CA specifications
		if(preg_match('/^([a-ceghj-npr-tv-z]){1}[0-9]{1}[a-ceghj-npr-tv-z]{1}[0-9]{1}[a-ceghj-npr-tv-z]{1}[0-9]{1}$/i',$zip)){
			return $zip;
		}else{
			return false;
		}
	}

	/**
	 * Validate that a password is in a good format
	 *
	 * @author Jacob Allred <jacob@jacoballred.com>
	 * @param string $password
	 * @return boolean
	 */
	public function Password($password){
		// Business rule: passwords must be 6 to 15 characters, alphanumeric
		// WebBalanced: 6 <= $password <= 15
		// Intelliverse: 1 <= $password <= 39, alphanumeric
		if($this->Alphanumeric($username,15,6) == true){
			return true;
		}else{
			return false;
		}
	}

	/**
	 * Validate that a username is in a good format
	 *
	 * @author Jacob Allred <jacob@jacoballred.com>
	 * @param string $username
	 * @return boolean
	 */
	public function Username($username){
		// Business rule: usernames must be 5 to 25 characters, alphanumeric
		// WebBalanced: 1 <= $username <= 25
		// Intelliverse: 5 <= $username <= 30
		if($this->Alphanumeric($username,25,5) == true){
			return true;
		}else{
			return false;
		}
	}

	/**
	 * Verify two variables match.
	 *
	 * @author Jacob Allred <jacob@jacoballred.com>
	 * @param mixed $string1
	 * @param mixed $string2
	 * @param boolean $strictMatch Match variable content and datatype
	 * @return boolean
	 */
	public function Matching($string1, $string2, $strictMatch = false){
		if($strictMatch == true){
			if($string1 === $string2){
				return true;
			}else{
				return false;
			}
		}else{
			if($string1 == $string2){
				return true;
			}else{
				return false;
			}
		}
	}

	/**
	 * Strip non-alpha characters.
	 *
	 * @author Jacob Allred <jacob@jacoballred.com>
	 * @param string $string
	 * @return string
	 */
	public function ReturnAlpha($string){
		$string = preg_replace('/[^A-Za-z]/','',$string);
		return $string;
	}

	/**
	 * Strip non-alpha/space characters.
	 *
	 * @author Jacob Allred <jacob@jacoballred.com>
	 * @param string $string
	 * @return string
	 */
	public function ReturnAlphaSpace($string){
		$string = preg_replace('/[^A-Za-z ]/','',$string);
		return $string;
	}

	/**
	 * Strip non-alphanumeric/space/hyphen characters.
	 *
	 * @author Jacob Allred <jacob@jacoballred.com>
	 * @param string $string
	 * @return string
	 */
	public function ReturnAlphanumericSpaceHyphen($string){
		$string = preg_replace('/[^A-Za-z0-9 \-]/','',$string);
		return $string;
	}

	/**
	 * Strip non-alphanumeric/space characters.
	 *
	 * @author Jacob Allred <jacob@jacoballred.com>
	 * @param string $string
	 * @return string
	 */
	public function ReturnAlphanumericSpace($string){
		$string = preg_replace('/[^A-Za-z0-9 ]/','',$string);
		return $string;
	}

	/**
	 * Strip non-alphanumeric characters.
	 *
	 * @author Jacob Allred <jacob@jacoballred.com>
	 * @param string $string
	 * @return string
	 */
	public function ReturnAlphanumeric($string){
		$string = preg_replace('/[^A-Za-z0-9]/','',$string);
		return $string;
	}

	/**
	 * Strip non-numeric characters.
	 *
	 * @author Jacob Allred <jacob@jacoballred.com>
	 * @param string $string
	 * @return string
	 */
	public function ReturnNumeric($string){
		$string = preg_replace('/[^0-9]/','',$string);
		return $string;
	}

	/**
	 * Validate alpha only
	 *
	 * @author Jacob Allred <jacob@jacoballred.com>
	 * @param string $string
	 * @param int $maxLength
	 * @param int $minimumLength
	 * @return boolean
	 */
	public function Alpha($string, $maxLength = 0, $minimumLength = 0){
		$count = preg_match('/[^A-Za-z]/',$string);
		if($count == 0){
			// Check for null input
			if(is_null($maxLength)){
				$maxLength = 0;
			}

			// Return false if too long or too short
			if(($maxLength > 0 && strlen($string) > $maxLength) || (strlen($string) < $minimumLength)){
				return false;
			}

			return true;
		}else{
			return false;
		}
	}

	/**
	 * Validate alpha/space only
	 *
	 * @author Jacob Allred <jacob@jacoballred.com>
	 * @param string $string
	 * @param int $maxLength
	 * @param int $minimumLength
	 * @return boolean
	 */
	public function AlphaSpace($string, $maxLength = 0, $minimumLength = 0){
		$count = preg_match('/[^A-Za-z ]/',$string);
		if($count == 0){
			// Check for null input
			if(is_null($maxLength)){
				$maxLength = 0;
			}

			// Return false if too long or too short
			if(($maxLength > 0 && strlen($string) > $maxLength) || (strlen($string) < $minimumLength)){
				return false;
			}

			return true;
		}else{
			return false;
		}
	}

	/**
	 * Validate alphanumeric only
	 *
	 * @author Jacob Allred <jacob@jacoballred.com>
	 * @param string $string
	 * @param int $maxLength
	 * @param int $minimumLength
	 * @return boolean
	 */
	public function Alphanumeric($string, $maxLength = 0, $minimumLength = 0){
		$count = preg_match('/[^A-Za-z0-9]/',$string);
		if($count == 0){
			// Check for null input
			if(is_null($maxLength)){
				$maxLength = 0;
			}

			// Return false if too long or too short
			if(($maxLength > 0 && strlen($string) > $maxLength) || (strlen($string) < $minimumLength)){
				return false;
			}

			return true;
		}else{
			return false;
		}
	}

	/**
	 * Validate alphanumeric/space/hyphen only.
	 * This was developed for use with Intelliverse's API.
	 *
	 * @author Jacob Allred <jacob@jacoballred.com>
	 * @param string $string
	 * @param int $maxLength
	 * @param int $minimumLength
	 * @return boolean
	 */
	public function AlphanumericSpaceHyphen($string, $maxLength = 0, $minimumLength = 0){
		$count = preg_match('/[^A-Za-z0-9 \-]/',$string);
		if($count == 0){
			// Check for null input
			if(is_null($maxLength)){
				$maxLength = 0;
			}

			// Return false if too long or too short
			if(($maxLength > 0 && strlen($string) > $maxLength) || (strlen($string) < $minimumLength)){
				return false;
			}

			return true;
		}else{
			return false;
		}
	}

	/**
	 * Validate alphanumeric/space only.
	 * This was developed for use with Intelliverse's API.
	 *
	 * @author Jacob Allred <jacob@jacoballred.com>
	 * @param string $string
	 * @param int $maxLength
	 * @param int $minimumLength
	 * @return boolean
	 */
	public function AlphanumericSpace($string, $maxLength = 0, $minimumLength = 0){
		$count = preg_match('/[^A-Za-z0-9 ]/',$string);
		if($count == 0){
			// Check for null input
			if(is_null($maxLength)){
				$maxLength = 0;
			}

			// Return false if too long or too short
			if(($maxLength > 0 && strlen($string) > $maxLength) || (strlen($string) < $minimumLength)){
				return false;
			}

			return true;
		}else{
			return false;
		}
	}

	/**
	 * Validate numeric only
	 *
	 * @author Jacob Allred <jacob@jacoballred.com>
	 * @param string $string
	 * @param int $maxLength
	 * @param int $minimumLength
	 * @return boolean
	 */
	public function Numeric($string, $maxLength = 0, $minimumLength = 0){
		if(is_numeric($string) == true){
			// Check for null input
			if(is_null($maxLength)){
				$maxLength = 0;
			}

			// Return false if too long or too short
			if(($maxLength > 0 && strlen($string) > $maxLength) || (strlen($string) < $minimumLength)){
				return false;
			}

			return true;
		}else{
			return false;
		}
	}

	/**
	 * Validate credit card number and return card type.
	 * Optionally you can validate if it is a specific type.
	 *
	 * @author Jacob Allred <jacob@jacoballred.com>
	 * @param string $ccnumber
	 * @param string $cardtype
	 * @param string $allowTest
	 * @return mixed
	 */
	public function CreditCard($ccnumber, $cardtype = '', $allowTest = false){
		// Check for test cc number
		if($allowTest == false && $ccnumber == '4111111111111111'){
			return false;
		}

		$ccnumber = $this->ReturnNumeric($ccnumber); // Strip non-numeric characters

		$creditcard = array(
			'visa'			=>	"/^4\d{3}-?\d{4}-?\d{4}-?\d{4}$/",
			'mastercard'	=>	"/^5[1-5]\d{2}-?\d{4}-?\d{4}-?\d{4}$/",
			'discover'		=>	"/^6011-?\d{4}-?\d{4}-?\d{4}$/",
			'amex'			=>	"/^3[4,7]\d{13}$/",
			'diners'		=>	"/^3[0,6,8]\d{12}$/",
			'bankcard'		=>	"/^5610-?\d{4}-?\d{4}-?\d{4}$/",
			'jcb'			=>	"/^[3088|3096|3112|3158|3337|3528]\d{12}$/",
			'enroute'		=>	"/^[2014|2149]\d{11}$/",
			'switch'		=>	"/^[4903|4911|4936|5641|6333|6759|6334|6767]\d{12}$/"
		);

		if(empty($cardtype)){
			$match=false;
			foreach($creditcard as $cardtype=>$pattern){
				if(preg_match($pattern,$ccnumber)==1){
					$match=true;
					break;
				}
			}
			if(!$match){
				return false;
			}
		}elseif(@preg_match($creditcard[strtolower(trim($cardtype))],$ccnumber)==0){
			return false;
		}

		$return['valid']	=	$this->LuhnCheck($ccnumber);
		$return['ccnum']	=	$ccnumber;
		$return['type']		=	$cardtype;
		return $return;
	}

	/**
	 * Do a modulus 10 (Luhn algorithm) check
	 *
	 * @author Jacob Allred <jacob@jacoballred.com>
	 * @param string $ccnum
	 * @return boolean
	 */
	public function LuhnCheck($ccnum){
		$checksum = 0;
		for ($i=(2-(strlen($ccnum) % 2)); $i<=strlen($ccnum); $i+=2){
			$checksum += (int)($ccnum{$i-1});
		}

		// Analyze odd digits in even length strings or even digits in odd length strings.
		for ($i=(strlen($ccnum)% 2) + 1; $i<strlen($ccnum); $i+=2){
			$digit = (int)($ccnum{$i-1}) * 2;
			if ($digit < 10){
				$checksum += $digit;
			}else{
				$checksum += ($digit-9);
			}
		}

		if(($checksum % 10) == 0){
			return true;
		}else{
			return false;
		}
	}

	/**
	 * Validate email address using a regular expression.
	 * We do not check to see if MX records exist because RFC 2821 says that when
	 * no mail exchangers are listed, hostname itself should be used as the only
	 * mail exchanger with a priority of 0.
	 *
	 * @author Jacob Allred <jacob@jacoballred.com>
	 * @param string $email
	 * @return boolean
	 */
	public function EmailAddress($email){
		// Regular expression to weed out obvious bad email addresses
		if(!eregi("^[_a-z0-9-]+(\.[_a-z0-9-]+)*@[a-z0-9-]+(\.[a-z0-9-]+)*(\.[a-z]{2,3})$", $email)){
			return false;
		}else{
			return true;
		}
	}

	/**
	 * Validate ABA Routing number is formatted properly.
	 * Does not strip invalid characters. Returns true or false.
	 *
	 * @author Jacob Allred <jacob@jacoballred.com>
	 * @param string $aba_code
	 * @param boolean $allowTest
	 * @return boolean
	 */
	public function ABACode($aba_code, $allowTest = false){
		// Converted to PHP from JS found at:
		// http://www.brainjar.com/js/validation/
		// See that site if you need more details on how ABA codes are validated.

		// Make sure it doesn't contain invalid characters
		if($aba_code != preg_replace('/[^0-9]/','',$aba_code)){
			return false;
		}

		// Check the length, it should be nine digits.
		if(strlen($aba_code) != 9){
			return false;
		}

		// Now run through each digit and calculate the total.
		$n = 0;
		for($i = 0; $i < 9; $i += 3) {
			$n += $aba_code[$i] * 3
			+ $aba_code[$i + 1] * 7
			+ $aba_code[$i + 2];
		}

		// If the resulting sum is an even multiple of ten (but not zero),
		// the aba routing number is good.
		if($n != 0 && $n % 10 == 0){
			// If $allowTest is false, then we don't want to allow the standard "test" ABA code
			// This prevents us from accepting dummy orders unless we explicitly want to.
			if($allowTest == false && $aba_code == '123456780'){
				return false;
			}else{
				return true;
			}
		}else{
			return false;
		}
	}

	/**
	 * Validate a date is in the future
	 *
	 * @author Jacob Allred <jacob@jacoballred.com>
	 * @param int $year
	 * @param mixed $month Accepts 1, Jan, january...
	 * @param int $day
	 * @param boolean $inclusive True to treat today's date as being in the future
	 * @return boolean
	 */
	public function FutureDate($year = null, $month = null, $day = null, $inclusive = true){
		// Make sure year is a number and that year is at least the current year
		if($year == null){
			 $year = date('Y');
		}elseif($this->Numeric($year) == false || $year < date('Y')){
			return false;
		}

		// Make sure month is a number
		$months = array(
			1 => 'January',
			2 => 'February',
			3 => 'March',
			4 => 'April',
			5 => 'May',
			6 => 'June',
			7 => 'July',
			8 => 'August',
			9 => 'September',
			10 => 'October',
			11 => 'November',
			12 => 'December'
		);
		$monthsShort = array(
			1 => 'Jan',
			2 => 'Feb',
			3 => 'Mar',
			4 => 'Apr',
			5 => 'May',
			6 => 'Jun',
			7 => 'Jul',
			8 => 'Aug',
			9 => 'Sep',
			10 => 'Oct',
			11 => 'Nov',
			12 => 'Dec'
		);
		if($month == null){
			$monthName = date('F');
			$monthNumber = date('n');
		}elseif($this->Numeric($month) == true){
			// Convert number to string
			if(isset($months[$month])){
				$monthName = $months[$month];
				$monthNumber = intval($month);
			}else{
				return false;
			}
		}else{
			// If it is a string, we need to see if it is a valid month name
			$month = ucwords(strtolower($month));
			$monthNumber = array_search($month,$months);
			if($monthNumber === false){
				$monthNumber = array_search($month, $monthsShort);
			}
			if($monthNumber === false){
				return false;
			}else{
				$monthName = $months[$monthNumber];
			}
		}

		// If day is null, then we are only matching month and year
		if($day == null){
			$today = strtotime(date("1 F Y"));
			$input = strtotime("1 $monthName $year");
		}else{
			// Make sure day is a number and is valid for the month we are in
			if($this->Numeric($day) == false){
				return false;
			}else{
				if($day > date('t', mktime(0, 0, 0, $monthNumber, 1, $year)) || $day < 1){
					return false;
				}
				$day = intval($day);
			}

			$today = strtotime(date("j F Y"));
			$input = strtotime("$day $monthName $year");
		}

		// See if input is greater than or equal to today
		if(($inclusive === true && $input >= $today) || ($inclusive === false && $input > $today)){
			return true;
		}else{
			return false;
		}
	}

}
?>

== class.http.php ==

<?php
/*
* Filename.......: class_http.php
* Author.........: Troy Wolf [troy@troywolf.com]
* Last Modified..: Date: 2006/03/06 10:15:00
* Description....: Screen-scraping class with caching. Includes image_cache.php
                   companion script. Includes static methods to extract data
                   out of HTML tables into arrays or XML. Now supports sending
                   XML requests and custom verbs with support for making
                   WebDAV requests to Microsoft Exchange Server.
*/

class http {
    var $log;
    var $dir;
    var $name;
    var $filename;
    var $url;
    var $port;
    var $verb;
    var $status;
    var $header;
    var $body;
    var $ttl;
    var $headers;
    var $postvars;
    var $xmlrequest;
    var $connect_timeout;
    var $data_ts;

    /*
    The class constructor. Configure defaults.
    */
    function http() {
        $this->log = "New http() object instantiated.<br />\n";

        /*
        Seconds to attempt socket connection before giving up.
        */
        $this->connect_timeout = 30;

        /*
        Set the 'dir' property to the directory where you want to store the cached
        content. I suggest a folder that is not web-accessible.
        End this value with a "/".
        */
        $this->dir = realpath("./")."/cache/"; //Default to current dir.

        $this->clean();

        return true;
    }

    /*
    fetch() method to get the content. fetch() will use 'ttl' property to
    determine whether to get the content from the url or the cache.
    */
    function fetch($url="", $ttl=0, $name="", $user="", $pwd="", $verb="GET") {
        $this->log .= "--------------------------------<br />fetch() called<br />\n";
        $this->log .= "url: ".$url."<br />\n";
        $this->status = "";
        $this->header = "";
        $this->body = "";
        if (!$url) {
            $this->log .= "OOPS: You need to pass a URL!<br />";
            return false;
        }
        $this->url = $url;
        $this->ttl = $ttl;
        $this->name = $name;
        $need_to_save = false;
        if ($this->ttl == "0") {
            if (!$fh = $this->getFromUrl($url, $user, $pwd, $verb)) {
                return false;
            }
        } else {
            if (strlen(trim($this->name)) == 0) { $this->name = MD5($url); }
            $this->filename = $this->dir."http_".$this->name;
            $this->log .= "Filename: ".$this->filename."<br />";
            $this->getFile_ts();
            if ($this->ttl == "daily") {
                if (date('Y-m-d',$this->data_ts) != date('Y-m-d',time())) {
                    $this->log .= "cache has expired<br />";
                    if (!$fh = $this->getFromUrl($url, $user, $pwd, $verb)) {
                        return false;
                    }
                    $need_to_save = true;
                    if ($this->getFromUrl()) { return $this->saveToCache(); }
                    } else {
                        if (!$fh = $this->getFromCache()) {
                        return false;
                    }
                }
            } else {
                if ((time() - $this->data_ts) >= $this->ttl) {
                    $this->log .= "cache has expired<br />";
                    if (!$fh = $this->getFromUrl($url, $user, $pwd, $verb)) {
                        return false;
                    }
                    $need_to_save = true;
                } else {
                    if (!$fh = $this->getFromCache()) {
                        return false;
                    }
                }
            }
        }

        /*
        Get response header.
        */
        $this->header = fgets($fh, 1024);
        $this->status = substr($this->header,9,3);
        while ((trim($line = fgets($fh, 1024)) != "") && (!feof($fh))) {
            $this->header .= $line;
            if ($this->status=="401" and strpos($line,"WWW-Authenticate: Basic realm=\"")===0) {
                fclose($fh);
                $this->log .= "Could not authenticate<br />\n";
                return FALSE;
            }
        }

        /*
        Get response body.
        */
        while (!feof($fh)) {
            $this->body .= fgets($fh, 1024);
        }
        fclose($fh);
        if ($need_to_save) { $this->saveToCache(); }
        return $this->status;
    }

    /*
    PRIVATE getFromUrl() method to scrape content from url.
    */
    function getFromUrl($url, $user="", $pwd="", $verb="GET") {
        $this->log .= "getFromUrl() called<br />";
        preg_match("~([a-z]*://)?([^:^/]*)(:([0-9]{1,5}))?(/.*)?~i", $url, $parts);
        $protocol = $parts[1];
        $server = $parts[2];
        $port = $parts[4];
        $path = $parts[5];
        if ($port == "") {
            if (strtolower($protocol) == "https://") {
                $port = "443";
            } else {
                $port = "80";
            }
        }

        if ($path == "") { $path = "/"; }

        if (!$sock = @fsockopen(((strtolower($protocol) == "https://")?"ssl://":"").$server, $port, $errno, $errstr, $this->connect_timeout)) {
            $this->log .= "Could not open connection. Error "
                .$errno.": ".$errstr."<br />\n";
            return false;
        }

        $this->headers["Host"] = $server.":".$port;

        if ($user != "" && $pwd != "") {
            $this->log .= "Authentication will be attempted<br />\n";
            $this->headers["Authorization"] = "Basic ".base64_encode($user.":".$pwd);
        }

        if (count($this->postvars) > 0) {
            $this->log .= "Variables will be POSTed<br />\n";
            $request = "POST ".$path." HTTP/1.0\r\n";
            $post_string = "";
            foreach ($this->postvars as $key=>$value) {
                $post_string .= "&".urlencode($key)."=".urlencode($value);
            }
            $post_string = substr($post_string,1);
            $this->headers["Content-Type"] = "application/x-www-form-urlencoded";
            $this->headers["Content-Length"] = strlen($post_string);
        } elseif (strlen($this->xmlrequest) > 0) {
            $this->log .= "XML request will be sent<br />\n";
            $request = $verb." ".$path." HTTP/1.0\r\n";
            $this->headers["Content-Length"] = strlen($this->xmlrequest);
        } else {
            $request = $verb." ".$path." HTTP/1.0\r\n";
        }

        #echo "<br />request: ".$request;

        if (fwrite($sock, $request) === FALSE) {
            fclose($sock);
            $this->log .= "Error writing request type to socket<br />\n";
            return false;
        }

        foreach ($this->headers as $key=>$value) {
            if (fwrite($sock, $key.": ".$value."\r\n") === FALSE) {
                fclose($sock);
                $this->log .= "Error writing headers to socket<br />\n";
                return false;
            }
        }

        if (fwrite($sock, "\r\n") === FALSE) {
            fclose($sock);
            $this->log .= "Error writing end-of-line to socket<br />\n";
            return false;
        }

        #echo "<br />post_string: ".$post_string;
        if (count($this->postvars) > 0) {
            if (fwrite($sock, $post_string."\r\n") === FALSE) {
                fclose($sock);
                $this->log .= "Error writing POST string to socket<br />\n";
                return false;
            }
        } elseif (strlen($this->xmlrequest) > 0) {
            if (fwrite($sock, $this->xmlrequest."\r\n") === FALSE) {
                fclose($sock);
                $this->log .= "Error writing xml request string to socket<br />\n";
                return false;
            }
        }

        return $sock;
    }

    /*
    PRIVATE clean() method to reset the instance back to mostly new state.
    */
    function clean()
    {
        $this->status = "";
        $this->header = "";
        $this->body = "";
        $this->headers = array();
        $this->postvars = array();
        /*
        Try to use user agent of the user making this request. If not available,
        default to IE6.0 on WinXP, SP1.
        */
        if (isset($_SERVER['HTTP_USER_AGENT'])) {
            $this->headers["User-Agent"] = $_SERVER['HTTP_USER_AGENT'];
        } else {
            $this->headers["User-Agent"] = "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)";
        }

        /*
        Set referrer to the current script since in essence, it is the referring
        page.
        */
        if (substr($_SERVER['SERVER_PROTOCOL'],0,5) == "HTTPS") {
            $this->headers["Referer"] = "https://".$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'];
        } else {
            $this->headers["Referer"] = "http://".$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'];
        }
    }

    /*
    PRIVATE getFromCache() method to retrieve content from cache file.
    */
    function getFromCache() {
        $this->log .= "getFromCache() called<br />";
        //create file pointer
        if (!$fp=@fopen($this->filename,"r")) {
            $this->log .= "Could not open ".$this->filename."<br />";
            return false;
        }
        return $fp;
    }

    /*
    PRIVATE saveToCache() method to save content to cache file.
    */
    function saveToCache() {
        $this->log .= "saveToCache() called<br />";

        //create file pointer
        if (!$fp=@fopen($this->filename,"w")) {
            $this->log .= "Could not open ".$this->filename."<br />";
            return false;
        }
        //write to file
        if (!@fwrite($fp,$this->header."\r\n".$this->body)) {
            $this->log .= "Could not write to ".$this->filename."<br />";
            fclose($fp);
            return false;
        }
        //close file pointer
        fclose($fp);
        return true;
    }

    /*
    PRIVATE getFile_ts() method to get cache file modified date.
    */
    function getFile_ts() {
        $this->log .= "getFile_ts() called<br />";
        if (!file_exists($this->filename)) {
            $this->data_ts = 0;
            $this->log .= $this->filename." does not exist<br />";
            return false;
        }
        $this->data_ts = filemtime($this->filename);
        return true;
    }

    /*
    Static method table_into_array()
    Generic function to return data array from HTML table data
    rawHTML: the page source
    needle: optional string to start parsing source from
    needle_within: 0 = needle is BEFORE table, 1 = needle is within table
    allowed_tags: list of tags to NOT strip from data, e.g. "<a><b>"
    */
    function table_into_array($rawHTML,$needle="",$needle_within=0,$allowed_tags="") {
        $upperHTML = strtoupper($rawHTML);
        $idx = 0;
        if (strlen($needle) > 0) {
            $needle = strtoupper($needle);
            $idx = strpos($upperHTML,$needle);
            if ($idx === false) { return false; }
            if ($needle_within == 1) {
                $cnt = 0;
                while(($cnt < 100) && (substr($upperHTML,$idx,6) != "<TABLE")) {
                    $idx = strrpos(substr($upperHTML,0,$idx-1),"<");
                    $cnt++;
                }
            }
        }
        $aryData = array();
        $rowIdx = 0;
        /*    If this table has a header row, it may use TD or TH, so
        check special for this first row. */
        $tmp = strpos($upperHTML,"<TR",$idx);
        if ($tmp === false) { return false; }
        $tmp2 = strpos($upperHTML,"</TR>",$tmp);
        if ($tmp2 === false) { return false; }
        $row = substr($rawHTML,$tmp,$tmp2-$tmp);
        $pattern = "/<TH>|<TH\ |<TD>|<TD\ /";
        preg_match($pattern,strtoupper($row),$matches);
        $hdrTag = $matches[0];

        while ($tmp = stripos($row,$hdrTag) !== false) {
            $tmp = stripos($row,">",$tmp);
            if ($tmp === false) { return false; }
            $tmp++;
            $tmp2 = stripos($row,"</T");
            $aryData[$rowIdx][] = trim(strip_tags(substr($row,$tmp,$tmp2-$tmp),$allowed_tags));
            $row = substr($row,$tmp2+5);
            preg_match($pattern,strtoupper($row),$matches);
            $hdrTag = $matches[0];
        }
        $idx = strpos($upperHTML,"</TR>",$idx)+5;
        $rowIdx++;

        /* Now parse the rest of the rows. */
        $tmp = strpos($upperHTML,"<TR",$idx);
        if ($tmp === false) { return false; }
        $tmp2 = strpos($upperHTML,"</TABLE>",$idx);
        if ($tmp2 === false) { return false; }
        $table = substr($rawHTML,$tmp,$tmp2-$tmp);

        while ($tmp = stripos($table,"<TR") !== false) {
            $tmp2 = stripos($table,"</TR");
            if ($tmp2 === false) { return false; }
            $row = substr($table,$tmp,$tmp2-$tmp);

            while ($tmp = stripos($row,"<TD") !== false) {
            $tmp = stripos($row,">",$tmp);
            if ($tmp === false) { return false; }
            $tmp++;
            $tmp2 = stripos($row,"</TD");
            $aryData[$rowIdx][] = trim(strip_tags(substr($row,$tmp,$tmp2-$tmp),$allowed_tags));
            $row = substr($row,$tmp2+5);
            }
            $table = substr($table,stripos($table,"</TR>")+5);
            $rowIdx++;
        }
        return $aryData;
    }

    /*
    Static method table_into_xml()
    Generic function to return xml dataset from HTML table data
    rawHTML: the page source
    needle: optional string to start parsing source from
    allowedTags: list of tags to NOT strip from data, e.g. "<a><b>"
    */
    function table_into_xml($rawHTML,$needle="",$needle_within=0,$allowedTags="") {
        if (!$aryTable = http::table_into_array($rawHTML,$needle,$needle_within,$allowedTags)) { return false; }
        $xml = "<?xml version=\"1.0\" standalone=\"yes\" \?\>\n";
        $xml .= "<TABLE>\n";
        $rowIdx = 0;
        foreach ($aryTable as $row) {
            $xml .= "\t<ROW id=\"".$rowIdx."\">\n";
            $colIdx = 0;
            foreach ($row as $col) {
                $xml .= "\t\t<COL id=\"".$colIdx."\">".trim(utf8_encode(htmlspecialchars($col)))."</COL>\n";
                $colIdx++;
            }
            $xml .= "\t</ROW>\n";
            $rowIdx++;
        }
        $xml .= "</TABLE>";
        return $xml;
    }
}

?>