Revision
22
Author
emsmith
Date
2006-10-13 09:53:49 -0700 (Fri, 13 Oct 2006)

Log Message

Throw up filter class

Added Paths

Diff

Added: library/trunk/lib/filter.class.php (21 => 22)


--- library/trunk/lib/filter.class.php	2006-09-24 01:07:26 UTC (rev 21)
+++ library/trunk/lib/filter.class.php	2006-10-13 16:53:49 UTC (rev 22)
@@ -0,0 +1,844 @@
+<?php
+/**
+ * filter.class.php - variable manipulation, force typing, input and output filtering
+ *
+ * This class is used everywhere to keep variables clean and shiny - notice this is not validation!
+ *
+ * This is released under the GPL, see license.txt for details
+ *
+ * @author       Elizabeth Smith <emsmith@callicore.net>
+ * @copyright    Elizabeth Smith (c)20050-2006
+ * @link         http://callicore.net
+ * @license      http://www.opensource.org/licenses/gpl-license.php GPL
+ * @version      $Id$
+ * @since        Php 5.1.0
+ * @package      callicore
+ * @subpackage   library
+ * @category     lib
+ * @filesource
+ */
+
+/**
+ * CC_Filter - variable typing and manipulation
+ *
+ * Some usage is similiar to new php filter extension - the general idea
+ * is input filtering (cookie/session/get/post/files/server), output filtering
+ * and string charset manipulation
+ *
+ * This does NOT do straight validation! instead it will sanitize all data!  If you
+ * want validation - use CC_Validate, eh?
+ *
+ * TODO: additional option of FORMAT in addition to strip, callback, et al
+ * format has pretty options in addition to strip functionality
+ */
+class CC_Filter
+{
+
+	//-------------- Input Source Constants -------------
+
+	/**
+	 * @const GET from $_GET
+	 */
+	const GET = 1;
+
+	/**
+	 * @const POST from $_POST and $_FILES
+	 */
+	const POST = 2;
+
+	/**
+	 * @const COOKIE from Kal_Http_Cookie (we don't use straight $_COOKIE)
+	 */
+	const COOKIE = 3;
+
+	/**
+	 * @const SESSION from Kal_Http_Session (we don't use php sessions at ALL)
+	 */
+	const SESSION = 4;
+
+	/**
+	 * @const SERVER from $_SERVER
+	 */
+	const SERVER = 5;
+
+	//-------------- Type Forcing Constants -------------
+
+	/**
+	 * @const INT regular integer
+	 */
+	const INT = 1;
+
+	/**
+	 * @const BOOL boolean
+	 * supports t, f, true, false , y, n, yes, no, on, off, 1, 0 (case insensitive)
+	 */
+	const BOOL = 2;
+
+	/**
+	 * @const FLOAT float
+	 */
+	const FLOAT = 3;
+
+	/**
+	 * @const STR string
+	 */
+	const STR = 4;
+
+	/**
+	 * @const ARR array(array is reserved word...sigh)
+	 */
+	const ARR = 5;
+
+	/**
+	 * @const OBJ object
+	 */
+	const OBJ = 6;
+
+	//-------------- Special Items -------------
+
+	/**
+	 * @const CALLBACK use function callback
+	 */
+	const CALLBACK = 7;
+
+	/**
+	 * @const REGEX use predefined or user regex
+	 */
+	const REGEX = 8;
+
+	/**
+	 * @const STRIP strip the string according to options
+	 */
+	const STRIP = 9;
+
+	/**
+	 * @const FORMAT format a string for display according to options
+	 */
+	const FORMAT = 10;
+
+	//-------------- Publicly Changeable -------------
+
+	/**
+	 * array of int -> string for mapping pathinfo vars to get vars
+	 * set this before calling getInput if using pathinfo with nonstandard map
+	 *
+	 * @var $map array
+	 */
+	public static $map = array('module', 'class', 'method', 'page');
+
+	/**
+	 * multi dimensional array with allowed tags and allowed attr
+	 *
+	 * @var $whitelist array
+	 */
+	public static $whitelist = array('a' => array('href'), 'span', 'br', 'p', 'attr' => array('class', 'title'));
+
+	//-------------- Internal only -------------
+
+	/**
+	 * regex to use for REGEX filter
+	 *
+	 * @var $regex array
+	 */
+	private static $regex = array(
+		'ucase' => '/[^A-Z]/', //all uppercase letters
+		'lcase' => '/[^a-z]/', //all lowercase letters
+		'alpha' => '/[^A-Za-z]/', //entire alphabet
+		'numeric' => '/[^0-9]/', //all numbers
+		'int' => '/[^0-9+-]/', //integer notation
+		'float' => '/[^0-9+-.,eE]/', //float notation including fractions, thousand, scientific notation
+		'alphanumeric' => '/[^A-Za-z0-9_]/', //all letters and numbers and underscore
+		'filename' => '/[^a-z0-9_-\.]/', //adds _-., no uppercase
+		'url' => '/[^A-Za-z0-9_-\.%\+]/', //adds % and + to filename
+		'email' => '/[a-zA-Z0-9"!#$%&\'*+-\/=?^_`{|}~@.\[\]]/', //valid items for email
+		'username' => '/[^A-Za-z0-9_-\.&#\*~\'",\|]/', //adds some special characters and space
+		'password' => '/[^A-Za-z0-9~!@#$%\^&\*\(\)-=\+:;\'",\.\?]/', //adds lots of special characters and space
+	);
+
+	/**
+	 * array of pathinfo vars
+	 *
+	 * @var $pathinfo array
+	 */
+	private static $pathinfo;
+
+	/**
+	 * magic quotes gpc status
+	 *
+	 * @var $magicquotes bool
+	 */
+	private static $magicquotes;
+
+	/**
+	 * magic_quotes_sybase status
+	 *
+	 * @var $sybasequotes bool
+	 */
+	private static $sybasequotes;
+
+	//----------------------------------------------------------------
+	//             Setup
+	//----------------------------------------------------------------
+
+	/**
+	 * private function setup
+	 * 
+	 * basically a static __construct
+	 *
+	 * @return void
+	 */
+	private static function setup()
+	{
+		self::$magicquotes = (bool) ini_get('magic_quotes_gpc');
+		self::$sybasequotes = (bool) ini_get('magic_quotes_sybase');
+		if(self::hasInput(self::SERVER, 'path_info'))
+		{
+			$pathinfo = self::input(self::SERVER, 'path_info', self::STRIP);
+		}
+		//if we're apache doing 404 redirect as rewriting we do this
+		elseif(self::hasInput(self::SERVER, 'redirect_status'))
+		{
+			$pathinfo = str_replace(self::input(self::SERVER, 'script_name', self::STRIP), '', self::input(self::SERVER, 'request_uri', self::STRIP));
+		}
+		//if we're IIS doing 404 redirect rewriting this can get messy
+		elseif(self::hasInput(self::SERVER, 'query_string') and strncmp('404;', self::input(self::SERVER, 'script_name', self::STRIP), 4) == 0)
+		{
+			$pathinfo = str_replace('404;'.((self::hasInput(self::SERVER, 'https') and self::input(self::SERVER, 'https', self::STRIP) != 'off') ?
+				'https' : 'http').'://'.$this->getInput(self::SERVER, 'server_name', self::STRIP)
+				.dirname(self::input(self::SERVER, 'orig_path_info', self::STRIP)).'/', '', self::input(self::SERVER, 'query_string', self::STRIP));
+		}
+		else
+		{
+			$pathinfo = '';
+		}
+		self::$pathinfo = preg_split('#/|//#u', $pathinfo, -1, PREG_SPLIT_NO_EMPTY);
+	}
+
+	//----------------------------------------------------------------
+	//             Actual public methods (all three of them)
+	//----------------------------------------------------------------
+
+	/**
+	 * public function input
+	 * 
+	 * grabs input
+	 *
+	 * @param type $name about
+	 * @return type about
+	 */
+	public static function input($source, $name, $filter, $default = NULL, $options = array())
+	{
+		if(is_null(self::$magicquotes))
+		{
+			self::setup();
+		}
+		if(self::hasInput($source, $name) == FALSE)
+		{
+			return $default;
+		}
+		$source = self::checkInput($source);
+		switch($source)
+		{
+			case(self::POST):
+			{
+				if(isset($_FILES[$name]))
+				{
+					$var = $_FILES[$name];
+				}
+				else
+				{
+					$var = self::deslash($_POST[$name]);
+				}
+				break;
+			}
+			case(self::COOKIE):
+			{
+				if(isset(Kalfu::$obj->cookie))
+				{
+					$var = Kalfu::$obj->cookie->$name;
+				}
+				else
+				{
+					$var = $_COOKIE[$name];
+				}
+				break;
+			}
+			case(self::SESSION):
+				if(isset(Kalfu::$obj->session))
+				{
+					$var = Kalfu::$obj->session->$name;
+				}
+				else
+				{
+					$var = $_SESSION[$name];
+				}
+				break;
+			case(self::SERVER):
+				$var = $_SERVER[strtoupper($name)];
+				break;
+			default:
+			{
+				if(isset($_GET[$name]))
+				{
+					$var = self::deslash($_GET[$name]);
+				}
+				else
+				{
+					$var = self::checkPathinfo($name);
+				}
+				break;
+			}
+		}
+		return self::dispatch($var, $filter, $options);
+	}
+
+	/**
+	 * public function hasInput
+	 * 
+	 * description
+	 *
+	 * @param type $name about
+	 * @return type about
+	 */
+	public static function hasInput($source, $name)
+	{
+		$source = self::checkInput($source);
+		// yeah switch is slow, bite me
+		switch($source)
+		{
+			case(self::POST):
+				$set = isset($_POST[$name]);
+				if($set == FALSE)
+				{
+					$set = isset($_FILES[$name]);
+				}
+				return $set;
+			case(self::COOKIE):
+				if(isset(Kalfu::$obj->cookie))
+				{
+					return isset(Kalfu::$obj->cookie->$name);
+				}
+				else
+				{
+					return isset($_COOKIE[$name]);
+				}
+			case(self::SESSION):
+				if(isset(Kalfu::$obj->session))
+				{
+					return isset(Kalfu::$obj->session->$name);
+				}
+				else
+				{
+					return isset($_SESSION[$name]);
+				}
+			case(self::SERVER):
+				return isset($_SERVER[strtoupper($name)]);
+			default:
+			{
+				$set = isset($_GET[$name]);
+				if($set == FALSE)
+				{
+					$set = self::checkPathinfo($name);
+				}
+				return $set;
+			}
+		}
+	}
+
+	/**
+	 * public function output
+	 * 
+	 * identical to input only instead of grabbing a superglobal/input value, the
+	 * variable is provided by the user
+	 *
+	 * @param mixed $var variable we're cleaning and checking
+	 * @param str|int $filter filter type to use
+	 * @return type about
+	 */
+	public static function output(&$var, $filter, $default = NULL, $options = array())
+	{
+		if(!isset($var))
+		{
+			return $default;
+		}
+		return self::dispatch($var, $filter, $options);
+	}
+
+	//----------------------------------------------------------------
+	//             Type Forcing Functions
+	//----------------------------------------------------------------
+
+	/**
+	 * private function toInt
+	 * 
+	 * casts whatever to an int
+	 *
+	 * @param mixed $var
+	 * @return int
+	 */
+	private static function toInt($var)
+	{
+		if(is_int($var))
+			{
+				return $var;
+			}
+			elseif(is_float($var))
+			{
+				return intval($var);
+			}
+			else
+			{
+				// settype does weird stuff, so we'll add 0 first
+				$var += 0;
+				// this is probably redundant
+				settype($var, 'int');
+				return $var;
+			}
+	}
+
+	/**
+	 * private function toBool
+	 * 
+	 * casts whatever to a boolean
+	 *
+	 * @param mixed $var
+	 * @return bool
+	 */
+	private static function toBool($var)
+	{
+		if(is_bool($var))
+		{
+			return $var;
+		}
+		else
+		{
+			//match all TRUE values case insensitively
+			if(preg_match('/t|true|y|yes|on/i', $var))
+			{
+				return TRUE;
+			}
+			//match all FALSE values case insensitively
+			elseif(preg_match('/f|false|n|no|off/i', $var))
+			{
+				return FALSE;
+			}
+			//otherwise you're doomed to php's settype guessing
+			else
+			{
+				settype($var, 'bool');
+				return $var;
+			}
+		}
+	}
+
+	/**
+	 * private function toFloat
+	 * 
+	 * casts whatever to a float
+	 *
+	 * @param mixed $var
+	 * @return float
+	 */
+	private static function toFloat($var)
+	{
+		if(is_float($var))
+		{
+			return $var;
+		}
+		else
+		{
+			return floatval($var);
+		}
+	}
+
+	/**
+	 * private function toString
+	 * 
+	 * casts whatever to a string AND TRIMS IT
+	 *
+	 * @param mixed $var
+	 * @return string
+	 */
+	private static function toString($var)
+	{
+		if(is_string($var))
+		{
+			return trim($var);
+		}
+		else
+		{
+			return trim(strval($var));
+		}
+	}
+
+	/**
+	 * private function toArray
+	 * 
+	 * forces whatever to an array
+	 *
+	 * @param mixed $var
+	 * @return array
+	 */
+	private static function toArray($var)
+	{
+		if(is_array($var))
+		{
+			return $var;
+		}
+		else
+		{
+			//we'll assume if there's an equal sign then it's parseable
+			if(preg_match('/^(.+)=(.+)$/', $var))
+			{
+				$array = array();
+				parse_str($var, $array);
+				return $array;
+			}
+			else
+			{
+				return array($var);
+			}
+		}
+	}
+
+	/**
+	 * private function toObject
+	 * 
+	 * casts whatever to an object
+	 *
+	 * @param mixed $var
+	 * @return string
+	 */
+	private static function toObject($var, $options = array())
+	{
+		if(!isset($options['class']))
+		{
+			$class = 'stdClass';
+		}
+		else
+		{
+			$class = $options['class'];
+		}
+		// remember - this will attempt to autoload classes so they better be somewhere
+		if(!(is_object($var) and $var instanceof $class))
+		{
+			//if this is NOT an object and it's not a stdclass, we'll pass the var as a constructor argument
+			if(!is_object($var) and $class == 'StdClass')
+			{
+				settype($var, 'object');
+			}
+			else
+			{
+				$var = new $class($var);
+			}
+		}
+		return $var;
+	}
+
+	//----------------------------------------------------------------
+	//             Special Items
+	//----------------------------------------------------------------
+
+	/**
+	 * private function doCallback
+	 * 
+	 * performs a callback
+	 *
+	 * @param mixed $callback
+	 * @return mixed
+	 */
+	private static function doCallback($var, $options = NULL)
+	{
+		if(!isset($options['callback']))
+		{
+			$callback = array(__CLASS__, 'toString');
+		}
+		else
+		{
+			$callback = $options['callback'];
+			unset($options['callback']);
+		}
+		$options = array_unshift($options, $var);
+		if(is_callable($callback))
+		{
+			$var = call_user_func_array($callback, $options);
+		}
+		return $var;
+	}
+
+	/**
+	 * private function doRegex
+	 * 
+	 * performs a regex replace call
+	 *
+	 * @param mixed $var
+	 * @return mixed
+	 */
+	private static function doRegex($var, $options = NULL)
+	{
+		if(isset($options['custom']))
+		{
+			$regex = '/'.preg_quote($options['custom'], '/').'/';
+		}
+		elseif(isset($options['filter']) and isset(self::$regex[$options['filter']]))
+		{
+			$regex = self::$regex[$options['filter']];
+		}
+		else
+		{
+			$regex = self::$regex['alphanumeric'];
+		}
+		return preg_replace($regex, '', $var);
+	}
+
+	/**
+	 * private function doStrip
+	 *
+	 * decodes options and farms out to appropriate methods
+	 *
+	 * @param mixed $var
+	 * @return mixed
+	 */
+	private static function doStrip($var, $options = NULL)
+	{
+		return $var;
+	}
+
+	/**
+	 * private function doFormat
+	 *
+	 * decodes options and farms out to appropriate methods
+	 *
+	 * @param mixed $var
+	 * @return mixed
+	 */
+	private function doFormat($var, $options = NULL)
+	{
+	}
+
+	//----------------------------------------------------------------
+	//             Helper Functions
+	//----------------------------------------------------------------
+
+	/**
+	 * private function dispatch
+	 *
+	 * matches string or int to make sure it's one of the desired types
+	 *
+	 * @param mixed $type type we're checking
+	 * @return int
+	 */
+	private static function dispatch($var, $type, $options)
+	{
+		if(is_int($type))
+		{
+			// yeah switch is slow, bite me
+			switch($type)
+			{
+				case(self::INT):
+					return self::toInt($var);
+				case(self::BOOL):
+					return self::toBool($var);
+				case(self::FLOAT):
+					return self::toFloat($var);
+				case(self::STR):
+					return self::toString($var);
+				case(self::ARR):
+					return self::toArray($var);
+				case(self::OBJ):
+					return self::toObject($var, $options);
+				case(self::CALLBACK):
+					return self::doCallback($var, $options);
+				case(self::REGEX):
+					return self::doRegex($var, $options);
+				case(self::FORMAT):
+					return self::doFormat($var, $options);
+				default:
+					return self::doStrip($var, $options);
+			}
+		}
+		else
+		{
+			// yeah switch is slow, bite me
+			switch(strtolower($type))
+			{
+				case('int'):
+				case('integer'):
+					return self::toInt($var);
+				case('bool'):
+				case('boolean'):
+					return self::toBool($var);
+				case('float'):
+				case('double'):
+				case('decimal'):
+					return self::toFloat($var);
+				case('str'):
+				case('string'):
+					return self::toString($var);
+				case('arr'):
+				case('array'):
+					return self::toArray($var);
+				case('obj'):
+				case('object'):
+					return self::toObject($var, $options);
+				case('call'):
+				case('callback'):
+				case('function'):
+					return self::doCallback($var, $options);
+				case('regex'):
+				case('filter'):
+				case('pcre'):
+					return self::doRegex($var, $options);
+				case('format'):
+				case('output'):
+				case('display'):
+					return self::doFormat($var, $options);
+				default:
+					return self::doStrip($var, $options);
+			}
+		}
+	}
+
+	/**
+	 * private function checkInput
+	 *
+	 * matches string or int to make sure it's one of the desired inputs
+	 *
+	 * @param mixed $type type we're checking
+	 * @return int
+	 */
+	private static function checkInput($source)
+	{
+		if(is_int($source))
+		{
+			// yeah switch is slow, bite me
+			switch($source)
+			{
+				case(self::POST):
+					return self::POST;
+				case(self::COOKIE):
+					return self::COOKIE;
+				case(self::SESSION):
+					return self::SESSION;
+				case(self::SERVER):
+					return self::SERVER;
+				default:
+					return self::GET;
+			}
+		}
+		else
+		{
+			// yeah switch is slow, bite me
+			switch(strtolower($source))
+			{
+				case('post'):
+					return self::POST;
+				case('cookie'):
+					return self::COOKIE;
+				case('session'):
+					return self::SESSION;
+				case('server'):
+					return self::SERVER;
+				default:
+					return self::GET;
+			}
+		}
+	}
+
+	/**
+	 * private function deslash
+	 * 
+	 * undoes any magic quotes
+	 *
+	 * @param mixed $var, var to clean
+	 * @return mixed cleaned var
+	 */
+	private static function deslash($var) 
+	{
+		if(self::$magicquotes or self::$sybasequotes)
+		{
+			if(is_scalar($var))
+			{
+				if(self::$sybasequotes)
+				{
+					$var = str_replace('\'\'', '\'', $var);
+				}
+				elseif(self::$magicquotes)
+				{
+					$var = stripslashes($var);
+				}
+			}
+			else
+			{
+				foreach($var as &$item)
+				{
+					$item = self::deslash($item);
+				}
+			}
+		}
+		return $var;
+	}
+
+	/**
+	 * private function getPathinfo
+	 *
+	 * given a get var -> return a mapped pathinfo spot
+	 *
+	 * @return void
+	 */
+	private static function getPathinfo($var)
+	{
+		// actually look for the pathinfo
+		if(in_array($var, self::$map))
+		{
+			$key = array_search($var, self::$map);
+			if(isset(self::$pathinfo[$key]))
+			{
+				return self::$pathinfo[$key];
+			}
+		}
+		return;
+	}
+
+	/**
+	 * private function getPathinfo
+	 *
+	 * given a get var -> return a mapped pathinfo spot
+	 *
+	 * @return void
+	 */
+	private static function checkPathinfo($var)
+	{
+		if(in_array($var, self::$map))
+		{
+			$key = array_search($var, self::$map);
+			if(isset(self::$pathinfo[$key]))
+			{
+				return TRUE;
+			}
+		}
+		return FALSE;
+	}
+
+
+	//----------------------------------------------------------------
+	//             Strip and Format sub-functions
+	//----------------------------------------------------------------
+
+// strip only: toTxt
+// strip only: toXhtml
+// strip only: force min/max length with chop/pad option
+
+// both: charset conversion (will need hard convert in kal_string)
+// both: specialchars/htmlentities (will need hard specialchars/entities in kal_string)
+// both: extended strip tags with normalize/tidy
+
+// format only: chop long words
+// format only: beautify html/xhtml
+// format only: indent
+// format only: encode emails
+// format only: linkify urls
+// format only: wordwrap
+// format only: format callback (for wiki/bbcode/textify/smilies or other markup -> html)
+}
+?>
Property changes on: library/trunk/lib/filter.class.php
___________________________________________________________________
Name: tsvn:logminsize
   + 15
Name: svn:keywords
   + Id