Login   Register  
PHP Classes
elePHPant
Icontem

File: checksum.class.php

Recommend this page to a friend!
Stumble It! Stumble It! Bookmark in del.icio.us Bookmark in del.icio.us
  Classes of Tom Schaefer  >  Checksum  >  checksum.class.php  >  Download  
File: checksum.class.php
Role: Class source
Content type: text/plain
Description: class for building and checking hyperlink checksums
Class: Checksum
Compute checksums of values passed by link URLs
Author: By
Last change: fix counter
Date: 2009-05-31 06:43
Size: 10,589 bytes
 

Contents

Class file image Download
<?php
define("SYSTEMKEY", "qfe78dsd3§%sd"); // your secret password
define("USE_DYNAMIC_PATTERN", true); // use dynamically build date patterns
/**
 * Checksum
 *
 * @author Thomas Schäfer
 * @desc time limited checksum builder
 * @example
 *
 * # build
 * // injected unlimited number of parameter arguments as string or integer
 * $checksumToLink = Checksum:: build ("YourFirstParameter"," YourSecondParameter"); // sample output aeeb0da600ab1d9934f1f065498c7497479cbd12
 * output view link:
 * <a href="http://www.your-domain.de/index.php?a=YourFirstParameter&b=YourSecondParameter&csm=$checksumToLink"></a>
 *
 * incoming request (e.g.):
 * $url = parse_url($_SERVER["REQUEST_URI"]);
 * parse_str($url["query"], $query);
 *
 * # proof
 * // inject the same number of parameter arguments as above with
 * // leading checksum string
 * $boolProof = Checksum::proof($query["csm"],$query["a"],$query["b"]);
 *
 * if($boolProof) echo "valid";
 * else echo "invalid";
 *
 * features:
 * - build
 * - proof
 * - setTimestamp(date)
 * - setValidTimeRange (seconds)
 * - setValidTimeRangeInDays (days)
 * - isValidFromTo (date, date)
 *
 */
class Checksum
{
	/**
	 * @const time shift constant in seconds for valid checksum
	 * desc should correspond with session validation time
	 */
	const DYN_PATTERN = USE_DYNAMIC_PATTERN; // true = dynamically build, false = individual pattern array
	private static $iCounter = 0;
	private static $strTimestamp = null;
	private static $strTimestampTo = null;
	private static $validTimeRange = 3600; // one hour valid
	/**
	 * checksum
	 * @desc build query checksum
	 * @return string 		new checksum
	 */
	public static function build()
	{
		$argCounter = func_num_args();
		if ($argCounter == 0)
		{
			throw new Exception("You need to define some string parameters for being able to build a checksum");
		}
		self::$iCounter = $argCounter;
		$arguments = func_get_args();
		$string = call_user_func(array (
				"self",
				"getRawKeyString"
			),
			$arguments
		);
		$time = self::getRawKeyTime();
		// value key

		$key = self::keyPattern($string, $time);
		return implode("", $key);
	}
	/**
	 * proof
	 * @desc proof checksum on string and time parts
	 * @param string reference 		referencing checksum
	 * @return bool
	 */
	public static function proof()
	{
		// checks
		$isValidChecksum = false;
		$isValidString = false;
		$isValidTime = false;
		$reference_checksum = null;
		if (func_num_args() <= 1)
		{
			throw new Exception("You need to define more then a single parameters");
		}

		$internal_arguments = array ();
		$arguments = func_get_args();
		foreach ($arguments as $argument)
		{
			if (preg_match('/[a-z0-9]{32}/', $argument))
			{
				$reference_checksum = $argument;
			}
			else
			{
				$internal_arguments[] = $argument;
			}
		}

		// new 	values
		$string = call_user_func(array (
			"self",
			"getRawKeyString"
			),
			$internal_arguments
		);
		$time = self::getRawKeyTime(); // time part

		// new value key
		$keyPattern = self::keyPattern($string, $time);

		// old values
		$timeParts = self::getTimeParts(); // positions of time parts (8,9)
		$stringReference = str_split($reference_checksum, 4);
		$timeReference = $stringReference[$timeParts[8]] . $stringReference[$timeParts[9]]; //
		$decodedTime = hexdec($timeReference);

		// begin validate time range
		$referingTime = time();
		if(
			!empty(Checksum::$strTimestamp) and
			!empty(Checksum::$strTimestampTo) and
			$referingTime >= Checksum::$strTimestamp and
			$referingTime <= Checksum::$strTimestampTo
		) {
			$isValidTime = true;
		} elseif(empty($isValidTime) and $referingTime - Checksum::$validTimeRange < $decodedTime){
			$isValidTime = true;
		}
		// end validate time range

		// check parts of old and new string value
		$isValidString = Checksum::validate($string, $time, $keyPattern, $stringReference);

		// check secondary conditions
		if ($isValidString and $isValidTime)
		{
			$isValidChecksum = true; // set primary condition => checksum is valid
		}
		return $isValidChecksum;
	}
	/**
	 * setTimestamp
	 * @param integer $timestamp
	 * @desc change the timestamp
	 */
	public static function setTimestamp($timestamp)
	{
		if (is_string($timestamp)){
			self::$strTimestamp = strtotime($timestamp);
		} else {
			throw new Exception("Timestamp has to be of type string, a valid date format which can be converted to unix time.");
		}
	}
	/**
	 * specify individual ranges
	 * @param integer $validTimeRange
	 */
	public static function setValidTimeRange($validTimeRange = null)
	{
		if (is_integer($validTimeRange) and $validTimeRange > -1) {
			self::$validTimeRange = $validTimeRange;
		} else {
			throw new Exception("Range has to be of type integer and greater than -1.");
		}
	}

	/**
	 * setValidTimeRangeInDays
	 * @param integer $validDays
	 */
	public static function setValidTimeRangeInDays($validDays = null)
	{
		selfValidTimeRange(3600*24*$validDays);
	}

	/**
	 * isValidFromTo
	 * @param string $dateFrom a valid timestamp, e.g.: 2009-01-01
	 * @param string $dateTo a valid timestamp, e.g.: 2009-02-01
	 */
	public static function isValidFromTo($dateFrom = null, $dateTo = null)
	{
		if (is_string($dateFrom) and is_string($dateTo))
		{
			$df = strtotime($dateFrom);
			$dt = strtotime($dateTo);
			if($dt>$df) {
				self::$strTimestamp = strtotime($dateFrom);
				self::$strTimestampTo = strtotime($dateTo);
			} else {
				throw new Exception("DateTo has to be greater than DateFrom");
			}
		}
		else
		{
			throw new Exception("Range has to be of type integer and greater than -1.");
		}
	}

	/**
	 * getRawKeyTime
	 * @desc splits hex time string into parts
	 * @return array
	 */
	private static function getRawKeyTime()
	{
		if (!empty (self::$strTimestamp))
		{
			return str_split(dechex(self::$strTimestamp), 4);
		}
		else
		{
			return str_split(dechex(time()), 4);
		}
	}
	/**
	 * getRawKeyString
	 * @desc creates hash (system key added) and splits md5 string into parts
	 * @return array
	 */
	private static function getRawKeyString()
	{
		$strRawKey = "";
		if (func_num_args() > 0)
		{
			$arguments = func_get_args();
			foreach ($arguments[0] as $argument)
			{
				$strRawKey .= $argument;
			}
		}
		else
		{
			throw Exception("No parameters had been injected.");
		}
		$strRawKey .= SYSTEMKEY;
		$string = str_split(md5($strRawKey), 4);
		return $string;
	}
	/**
	 * drawPatternByDay
	 * @desc returns pattern orders for each day
	 * @param integer $indexDayPattern
	 * @return array
	 */
	private static function drawPatternByDay($indexDayPattern)
	{
		switch (self::DYN_PATTERN)
		{
			// dynamically build date pattern
			case true :
				$num = date("Ym");
				$t = date("t");
				$range = array ();
				$arrSort = array ();
				$pattern = array ();
				for ($i = 1; $i <= $t; $i++)
				{
					$date = strtotime(date("Ym") . str_pad($i, 2, "0", STR_PAD_LEFT));
					$str = str_split(md5($date), 4);
					foreach ($str as $index => $val)
					{
						$range[$i][$index] = $val;
					}
					$weekday = date("w", $date);
					$parts = str_split(md5(strtotime(date("Ym"))), 4);
					$range[$i][8] = $str[8] = $parts[$weekday];
					$range[$i][9] = $str[9] = $parts[$weekday +1];
					sort($str);
					$array = array_flip($range[$i]);
					foreach ($str as $key => $val)
					{
						$pattern[$i][$key] = $array[$val];
					}
				}
				return ($pattern[$indexDayPattern -1]);
			default :
				/*
				 * positions: 0 - 7 => string parts 8 - 9 => time parts shifting parts
				 * by reordering range
				 */
			$patterns = array(
				array(0,2,4,6,8,1,3,5,7,9),
				array(1,3,5,7,9,2,4,6,8,0),
				array(2,4,6,8,0,3,5,7,9,1),
				array(3,5,7,9,1,2,4,6,8,0),
				array(2,4,6,8,1,3,5,7,9,0),
				array(7,9,0,2,4,6,8,1,3,5),
				array(1,2,4,6,8,0,3,5,7,9),
				array(8,0,3,5,7,1,2,4,6,9),
				array(5,7,2,4,6,8,0,3,9,1),
				array(6,8,0,3,5,7,2,4,9,1),
				array(8,0,3,5,6,7,2,4,9,1),
				array(7,2,4,9,8,0,3,5,6,1),
				array(2,0,4,6,8,1,3,5,7,9),
				array(1,3,7,5,9,2,4,6,8,0),
				array(2,4,8,6,0,3,5,7,9,1),
				array(3,5,9,7,1,2,4,6,8,0),
				array(2,4,6,8,1,5,3,7,9,0),
				array(7,9,0,2,6,4,8,1,3,5),
				array(1,2,4,6,8,0,5,3,7,9),
				array(8,0,3,5,7,1,2,4,6,9),
				array(5,7,2,4,6,3,0,8,9,1),
				array(6,8,4,3,5,7,2,0,9,1),
				array(8,2,3,5,6,7,0,4,9,1),
				array(7,2,1,9,5,0,3,8,6,4),
				array(5,7,2,4,1,8,0,3,9,6),
				array(6,8,9,3,5,2,7,4,0,1),
				array(8,0,5,3,6,7,4,2,9,1),
				array(4,2,6,0,8,3,5,7,9,1),
				array(3,1,5,2,9,7,4,6,8,0),
				array(1,2,4,0,8,6,3,5,7,9),
				array(0,8,3,5,7,2,1,4,6,9),
			);

			return ($patterns[$indexDayPattern-1]);
		}
	}
	/**
	 * keyPattern
	 * @desc re-orders the key map by pattern
	 * @param array $string
	 * @param array $key
	 * @return array
	 */
	private static function keyPattern($string, $time)
	{
		$indexDayPattern = self::drawPatternByDay(date("d"));
		$merge = array_merge($string, $time);
		$newPatternOrder = array ();
		foreach ($indexDayPattern as $index)
		{
			$newPatternOrder[] = $merge[$index];
		}
		return $newPatternOrder;
	}
	/**
	 * getTimeParts
	 * @desc returns array of positions of time parts
	 * return array
	 */
	private static function getTimeParts()
	{
		$pattern = self::drawPatternByDay(date("d"));
		$timeParts = array ();
		foreach ($pattern as $key => $index)
		{
			switch ($index)
			{
				// time parts
				case 9 :
				case 8 :
					$timeParts[$index] = $key;
					break;
					// string parts
				default :
					break;
			}
		}
		return $timeParts;
	}
	/**
	 * validate
	 * @desc checks against pattern if the checksum is valid
	 * @param array $string
	 * @param array $time
	 * @param array $key 					new value key
	 * @param array $stringReference		old value key
	 * return bool
	 */
	private static function validate($string, $time, $key, $stringReference)
	{
		$pattern = self::drawPatternByDay(date("d"));
		$intCounter = 1;
		foreach ($pattern as $index)
		{
			switch ($index)
			{
				// time parts
				case 9 :
				case 8 :
					break;
					// string parts
				default :
					if ($key[$index] == $stringReference[$index])
					{
						$intCounter++;
					}
					break;
			}
		}
		if ($intCounter >= 7)
		{ // all string parts are equal
			return true;
		}
		return false;
	}
}