View file veppa_wallpaper/sys/Framework.php

File size: 54.06Kb
<?php

/**
 * Framework : Extra Light PHP Framework
 * http://www.madebyfrog.com/framework/
 *
 * Copyright (c) 2007, Philippe Archambault
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 *
 * @author Philippe Archambault <philippe.archambault@gmail.com>
 * @copyright 2007 Philippe Archambault
 * @package Framework
 * @version 1.6
 * @license http://www.opensource.org/licenses/mit-license.html MIT License
 *
 * UPDATES:
 * Vepa Halliyev (www.veppa.com) - 04.05.2008
 *   - removed validation. use as static class (13 sept 2008)
 *
 *   - Added validation function to view and controller. Requires valitation class in helper folder.
 *   - Inflator::slugify to create google friendly permalinks
 */

define('FRAMEWORK_STARTING_MICROTIME', get_microtime());

// all constants that you can define before to costumize your framework
if (!defined('DEBUG'))              define('DEBUG', false);

if (!defined('CORE_ROOT'))          define('CORE_ROOT',   dirname(__FILE__));
if (!defined('APP_PATH'))           define('APP_PATH',    CORE_ROOT.DIRECTORY_SEPARATOR.'app');
if (!defined('HELPER_PATH'))        define('HELPER_PATH', CORE_ROOT.DIRECTORY_SEPARATOR.'helpers');

if (!defined('DOMAIN'))           	define('DOMAIN', dirname($_SERVER['HTTP_HOST'].$_SERVER['SCRIPT_NAME']));
if (!defined('BASE_URL'))           define('BASE_URL', 'http://'. DOMAIN .'/?/');

if (!defined('DEFAULT_CONTROLLER')) define('DEFAULT_CONTROLLER', 'index');
if (!defined('DEFAULT_ACTION'))     define('DEFAULT_ACTION', 'index');

// setting error display depending on debug mode or not
error_reporting((DEBUG ? E_ALL ^ E_NOTICE : 0));

// no more quotes escaped with a backslash
if (PHP_VERSION < '5.3')
@set_magic_quotes_runtime(0);

// start session 
if ( ! isset($_SESSION))
	session_start();

ini_set('date.timezone', DEFAULT_TIMEZONE);
if(function_exists('date_default_timezone_set'))
date_default_timezone_set(DEFAULT_TIMEZONE);
else
putenv('TZ='.DEFAULT_TIMEZONE);

/**
 * The Dispatcher main Core class is responsible for mapping urls /
 * routes to Controller methods. Each route that has the same number of directory
 * components as the current requested url is tried, and the first method that
 * returns a response with a non false / non null value will be returned via the
 * Dispatcher::dispatch() method. For example:
 *
 * A route string can be a literal url such as '/pages/about' or contain
 * wildcards (:any or :num) and/or regex like '/blog/:num' or '/page/:any'.
 *
 * Dispatcher::addRoute(array(
 *  '/' => 'page/index',
 *  '/about' => 'page/about,
 *  '/blog/:num' => 'blog/post/$1',
 *  '/blog/:num/comment/:num/delete' => 'blog/deleteComment/$1/$2'
 * ));
 *
 * Visiting /about/ would call PageController::about(),
 * visiting /blog/5 would call BlogController::post(5)
 * visiting /blog/5/comment/42/delete would call BlogController::deleteComment(5,42)
 *
 * The dispatcher is used by calling Dispatcher::addRoute() to setup the route(s),
 * and Dispatcher::dispatch() to handle the current request and get a response.
 */
final class Dispatcher
{
	private static $routes = array();
	private static $params = array();
	private static $status = array();
	private static $requested_url = null;

	public static function addRoute($route, $destination=null)
	{
		if ($destination != null && !is_array($route)) {
			$route = array($route => $destination);
		}
		self::$routes = array_merge(self::$routes, $route);
	}

	public static function splitUrl($url)
	{
		return preg_split('/\//', $url, -1, PREG_SPLIT_NO_EMPTY);
	}

	public static function dispatch($requested_url=null)
	{
		Flash::init();

		$requested_url = self::setCurrentUrl($requested_url);


		 
		// this is only trace for debuging
		self::$status['requested_url'] = $requested_url;

		// make the first split of the current requested_url
		self::$params = self::splitUrl($requested_url);

		// do we even have any custom routing to deal with?
		if (count(self::$routes) === 0) {
			return self::executeAction(self::getController(), self::getAction(), self::getParams());
		}

		// is there a literal match? If so we're done
		if (isset(self::$routes[$requested_url])) {
			self::$params = self::splitUrl(self::$routes[$requested_url]);
			return self::executeAction(self::getController(), self::getAction(), self::getParams());
		}

		// loop through the route array looking for wildcards
		foreach (self::$routes as $route => $uri) {
			// convert wildcards to regex
			if (strpos($route, ':') !== false) {
				$route = str_replace(':any', '(.+)', str_replace(':num', '([0-9]+)', $route));
			}
			// does the regex match?
			if (preg_match('#^'.$route.'$#', $requested_url)) {
				// do we have a back-reference?
				if (strpos($uri, '$') !== false && strpos($route, '(') !== false) {
					$uri = preg_replace('#^'.$route.'$#', $uri, $requested_url);
				}
				self::$params = self::splitUrl($uri);
				// we fund it, so we can break the loop now!
				break;
			}
		}

		return self::executeAction(self::getController(), self::getAction(), self::getParams());
	} // dispatch

	public static function getCurrentUrl()
	{
		if(self::$requested_url === null)
		{
			return self::setCurrentUrl();
		}
		else
		{
			return self::$requested_url;
		}
	}

	// set cyurrent url here if we need to use current url before dispatch for language
	// and predispatch actions
	public static function setCurrentUrl($requested_url=null)
	{
		// if no url passed, we will get the first key from the _GET array
		// that way, index.php?/controller/action/var1&email=example@example.com
		// requested_url will be equal to: /controller/action/var1
		if ($requested_url === null) {
			
			if(self::$requested_url !== null)
        	{
        		return self::$requested_url;
        	}
			
			//$requested_url = count($_GET) >= 1 ? key($_GET) : '/';
			$pos = strpos($_SERVER['QUERY_STRING'], '&');
			if ($pos !== false) {
	            $requested_url = substr($_SERVER['QUERY_STRING'], 0, $pos);
			} else {
				$requested_url = $_SERVER['QUERY_STRING'];
			}
		}

		// requested url MUST start with a slash (for route convention)
		$requested_url = '/'.trim($requested_url,'/').'/';
		/*if (strpos($requested_url, '/') !== 0) {
		 $requested_url = '/' . $requested_url;
		 }*/
		if(strpos($requested_url,'.')!==false)
		{
			$requested_url = rtrim($requested_url,'/');
		}

		self::$requested_url = $requested_url;

		return self::$requested_url;
	}

	public static function getController()
	{
		return isset(self::$params[0]) ? self::$params[0]: DEFAULT_CONTROLLER;
	}

	public static function getAction()
	{
		return isset(self::$params[1]) ? self::$params[1]: DEFAULT_ACTION;
	}

	public static function getParams()
	{
		return array_slice(self::$params, 2);
	}

	public static function getStatus($key=null)
	{
		return ($key === null) ? self::$status: (isset(self::$status[$key]) ? self::$status[$key]: null);
	}

	public static function executeAction($controller, $action, $params)
	{
		//echo "executeAction($controller, $action, $params)";
		 
		self::$status['controller'] = $controller;
		self::$status['action'] = $action;
		self::$status['params'] = implode(', ', $params);

		$controller_class = Inflector::camelize($controller);
		$controller_class_name = $controller_class . 'Controller';

		// get a instance of that controller
		if (class_exists($controller_class_name)) {
			$controller = new $controller_class_name();
		} else {
			//page_not_found();
		}
		if ( ! $controller instanceof Controller) {
			throw new Exception("Class '{$controller_class_name}' does not extends Controller class!");
		}

		// execute the action
		$controller->execute($action, $params);
	}

	// return test.xx.com
	public static function getServer()
	{
		return strtolower($_SERVER['SERVER_NAME']);
	}

	// return /yazi/test.html
	public static function getScript($is_file = true)
	{
		//return strtolower($_SERVER['SCRIPT_NAME']);
		//$return = implode('/',array_slice(self::$params, 1));
		 
		$return = implode('/',self::getParams());

		if(strpos($return,'.')===false)
		{
			if($is_file)
			{
				$return .= '/index.html';
			}
			else
			{
				$return .= '/';
			}
		}
		 
		return $return;
	}

} // end Dispatcher class


/**
 * Used for database table objects. read, sate, update, delete.
 *
 * @author Vepa Halliyev - veppa.com (updates)
 * @version 1.0
 *
 *
 * Updates:
 * 02.05.2008 Added $col static variable to add security to table columns and prevent unsuspected errors.
 * 02.06.2008 Updated class to use simple point for queries. Use query to connenct just before query done.
 * 			  Added functionality to handle multiple connections. master-slave. slaves have to specify explicitly
 *
 */
class Record
{
	const PARAM_BOOL = 5;
	const PARAM_NULL = 0;
	const PARAM_INT = 1;
	const PARAM_STR = 2;
	const PARAM_LOB = 3;
	const PARAM_STMT = 4;
	const PARAM_INPUT_OUTPUT = -2147483648;
	const PARAM_EVT_ALLOC = 0;
	const PARAM_EVT_FREE = 1;
	const PARAM_EVT_EXEC_PRE = 2;
	const PARAM_EVT_EXEC_POST = 3;
	const PARAM_EVT_FETCH_PRE = 4;
	const PARAM_EVT_FETCH_POST = 5;
	const PARAM_EVT_NORMALIZE = 6;

	const FETCH_LAZY = 1;
	const FETCH_ASSOC = 2;
	const FETCH_NUM = 3;
	const FETCH_BOTH = 4;
	const FETCH_OBJ = 5;
	const FETCH_BOUND = 6;
	const FETCH_COLUMN = 7;
	const FETCH_CLASS = 8;
	const FETCH_INTO = 9;
	const FETCH_FUNC = 10;
	const FETCH_GROUP = 65536;
	const FETCH_UNIQUE = 196608;
	const FETCH_CLASSTYPE = 262144;
	const FETCH_SERIALIZE = 524288;
	const FETCH_PROPS_LATE = 1048576;
	const FETCH_NAMED = 11;

	const ATTR_AUTOCOMMIT = 0;
	const ATTR_PREFETCH = 1;
	const ATTR_TIMEOUT = 2;
	const ATTR_ERRMODE = 3;
	const ATTR_SERVER_VERSION = 4;
	const ATTR_CLIENT_VERSION = 5;
	const ATTR_SERVER_INFO = 6;
	const ATTR_CONNECTION_STATUS = 7;
	const ATTR_CASE = 8;
	const ATTR_CURSOR_NAME = 9;
	const ATTR_CURSOR = 10;
	const ATTR_ORACLE_NULLS = 11;
	const ATTR_PERSISTENT = 12;
	const ATTR_STATEMENT_CLASS = 13;
	const ATTR_FETCH_TABLE_NAMES = 14;
	const ATTR_FETCH_CATALOG_NAMES = 15;
	const ATTR_DRIVER_NAME = 16;
	const ATTR_STRINGIFY_FETCHES = 17;
	const ATTR_MAX_COLUMN_LEN = 18;
	const ATTR_EMULATE_PREPARES = 20;
	const ATTR_DEFAULT_FETCH_MODE = 19;

	const ERRMODE_SILENT = 0;
	const ERRMODE_WARNING = 1;
	const ERRMODE_EXCEPTION = 2;
	const CASE_NATURAL = 0;
	const CASE_LOWER = 2;
	const CASE_UPPER = 1;
	const NULL_NATURAL = 0;
	const NULL_EMPTY_STRING = 1;
	const NULL_TO_STRING = 2;
	const ERR_NONE = '00000';
	const FETCH_ORI_NEXT = 0;
	const FETCH_ORI_PRIOR = 1;
	const FETCH_ORI_FIRST = 2;
	const FETCH_ORI_LAST = 3;
	const FETCH_ORI_ABS = 4;
	const FETCH_ORI_REL = 5;
	const CURSOR_FWDONLY = 0;
	const CURSOR_SCROLL = 1;
	const MYSQL_ATTR_USE_BUFFERED_QUERY = 1000;
	const MYSQL_ATTR_LOCAL_INFILE = 1001;
	const MYSQL_ATTR_INIT_COMMAND = 1002;
	const MYSQL_ATTR_READ_DEFAULT_FILE = 1003;
	const MYSQL_ATTR_READ_DEFAULT_GROUP = 1004;
	const MYSQL_ATTR_MAX_BUFFER_SIZE = 1005;
	const MYSQL_ATTR_DIRECT_QUERY = 1006;


	public static $__CONNS__ = false;
	public static $__CONNS_IMPLODE__ = false;
	public static $__QUERIES__ = array();
	public static $__QUERY_COUNT__ = 0;
	public static $__CONNECTIONS__ = array();

	private $cols = array();// column names for secure data inserts
	public $locale_db;// database connection for different languages. tr,en ...


	/*final public static function connection($connection)
	 {
	 self::getConnection($con_type) = $connection;
	 }*/

	 
	/**
	 * Gets database connection to execute queries. Always use this function to get db object
	 *
	 * @param string $type database connection type. default is master, can choose if has several connections
	 * @return PDO DB connection object
	 */
	final public static function getConnection($con_type)
	{
		
		if(!self::$__CONNS__[$con_type])
		{
			// chek if connection has several alternatives. connect to one
			if(isset(self::$__CONNECTIONS__[$con_type][0]))
			{
				// choose random connection
				$con_id = rand(0,count(self::$__CONNECTIONS__[$con_type]));
				$con = self::$__CONNECTIONS__[$con_type][$con_id];
			}
			else
			{
				$con = self::$__CONNECTIONS__[$con_type];
			}

			if(!$con)
			{
				// if it is read then check normal connection 
				if(strpos($con_type,'READ_') === 0)
				{
					return self::getConnection(substr($con_type,5));
				}
				
				throw new Exception("Connection type '{$con_type}' not found!");
			}

			// check if same connection is already connected
			//echo $con_type;
			$key = implode(',',$con);
			if(!self::$__CONNS_IMPLODE__[$key])
			{
				// connect to db
				 
				extract($con);

				//echo '['.$key.']';
				 
				try {
					// if no connection estabilish one
					if (USE_PDO)
					{
						//PDO::setAttribute(self::ATTR_TIMEOUT,2);
						//echo '['.PDO::getAttribute(self::ATTR_TIMEOUT).']';
						$__FROG_CONN__ = new PDO($DB_DSN, $DB_USER, $DB_PASS);
						$__FROG_CONN__->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, true);
					}
					else
					{
						require_once CORE_ROOT . '/libraries/DoLite.php';
						$__FROG_CONN__ = new DoLite($DB_DSN, $DB_USER, $DB_PASS);
					}
				
				} catch (Exception $e) {
					header_503();
					//echo 'Ops.. something went wrong with our servers. Please let us know if we havent fixed it in one hour. ';
					//echo (is_dev()?$e->getMessage():'DB connection error');
					echo 'DB connection error';
					exit;
				}

				self::$__CONNS_IMPLODE__[$key] = $__FROG_CONN__;
				self::$__CONNS_IMPLODE__[$key]->exec("set names 'utf8'");

				if(DEBUG && USE_PDO)
				{
					$__FROG_CONN__->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION );
				}

			}

			self::$__CONNS__[$con_type] = self::$__CONNS_IMPLODE__[$key];
		}
		 
		if(!self::$__CONNS__[$con_type])
		{
			throw new Exception('Invalid db connection');
		}
		 
		return self::$__CONNS__[$con_type];
	}

	final public static function logQuery($sql,$con_type='master')
	{
		self::$__QUERIES__[$con_type][] = $sql;
		self::$__QUERY_COUNT__++;
	}

	final public static function getQueryLog()
	{
		return self::$__QUERIES__;
	}

	final public static function getQueryCount()
	{
		return self::$__QUERY_COUNT__;
	}

	final public static function query($sql, $values=false,$con_type = 'master')
	{
		self::logQuery($sql,$con_type);

		Benchmark::cp();

		$sql_lower = strtolower($sql);
		$is_update = ( strpos($sql_lower,'insert')===0 || strpos($sql_lower,'delete')===0 );


		if (is_array($values)) {
			$stmt = self::getConnection($con_type)->prepare($sql);
			$stmt->execute($values);
			Benchmark::cp($sql.':'.$con_type.':'.implode(',',$values));
			if($is_update)
			{
				$r = true;
			}
			else
			{
				$r = @$stmt->fetchAll(self::FETCH_OBJ);
			}
		} else {
			$r = self::getConnection($con_type)->exec($sql) !== false;
			Benchmark::cp($sql.':'.$con_type);
		}
		 
		return $r;
	}


	public static function queryOne($sql,$values=false,$con_type='master')
	{
		self::logQuery($sql,$con_type);

		Benchmark::cp();

		if (is_array($values)) {
			$stmt = self::getConnection($con_type)->prepare($sql.' LIMIT 0,1');
			$stmt->execute($values);
			Benchmark::cp($sql.':'.$con_type.':'.implode(',',$values));
			$r = $stmt->fetchAll(self::FETCH_OBJ);
			$r = $r[0];
		} else {
			$r = self::getConnection($con_type)->exec($sql) !== false;
			Benchmark::cp($sql.':'.$con_type);
		}

		return $r;
	}


	final public static function write($sql,$values=false,$con_type='master')
	{
		return self::query($sql,$values,$con_type);
	}

	final public static function read($sql,$values=false,$con_type='master')
	{
		return self::query($sql,$values,'READ_'.$con_type);
	}
		
	final public static function queryArraySingle($sql, $values=false,$con_type = 'master',$field='id')
	{
		// always read
		$r = array();
		$result = self::read($sql,$values,$con_type);
		if($result)
		{
			foreach($result as $record)
			{
				$r[] = $record->{$field};
			}
		}
		return $r;
	}

	final public static function fetchColumn($sql, $values=array(),$con_type = 'master')
	{
		Benchmark::cp();
		 
		// aslways read
		$con_type = 'READ_'.$con_type;
		 
		self::logQuery($sql,$con_type);

		$stmt = self::getConnection($con_type)->prepare($sql);
		$stmt->execute($values);

		Benchmark::cp($sql.':'.$con_type.':'.implode(',',$values));

		return $stmt->fetchColumn();
	}

	final public static function tableNameFromClassName($class_name)
	{
		try
		{
			if (class_exists($class_name) && defined($class_name.'::TABLE_NAME'))
			return TABLE_PREFIX.constant($class_name.'::TABLE_NAME');
		}
		catch (Exception $e)
		{
			return TABLE_PREFIX.Inflector::underscore($class_name);
		}
	}
	
	final public static function classNameFromClassName($class_name)
	{
		try
		{
			if (class_exists($class_name))
			return $class_name;
		}
		catch (Exception $e)
		{
			return 'stdClass';
		}
	}

	final public static function escape($value,$con_type = 'master')
	{
		return self::getConnection($con_type)->quote($value);
	}

	final public static function lastInsertId($con_type = 'master')
	{
		return self::getConnection($con_type)->lastInsertId();
	}

	public function __construct($data = false, $locale_db = null)
	{
		$this->locale_db = $locale_db;
		if (is_array($data)) {
			$this->setFromData($data);
		}
	}


	public function setFromData($data)
	{
		foreach($data as $key => $value) {
			$this->$key = $value;
		}
	}

	/**
	 * Generates a insert or update string from the supplied data and execute it
	 *
	 * @return boolean
	 */
	public function save($key='id',$con_type='master')
	{
		Benchmark::cp();

		if ( ! $this->beforeSave()) return false;

		$value_of = array();

		if (empty($this->{$key})) {

			// unset index value
			unset($this->{$key});
			 
			if ( ! $this->beforeInsert()) return false;

			$columns = $this->getColumns();

			// escape and format for SQL insert query
			foreach ($columns as $column) {
				if ($this->isColumn($column)) {
					$value_of[$column] = self::getConnection($con_type)->quote($this->$column);
				}
			}

			$sql = 'INSERT INTO '.self::tableNameFromClassName(get_class($this)).' ('
			. implode(', ', array_keys($value_of)).') VALUES ('.implode(', ', array_values($value_of)).')';
			//Flash::set('error', $sql);
			$return = self::getConnection($con_type)->exec($sql) !== false;
			$this->{$key} = self::lastInsertId($con_type);
			 
			if ( ! $this->afterInsert()) return false;

		} else {
			 
			if ( ! $this->beforeUpdate()) return false;

			$columns = $this->getColumns();

			// escape and format for SQL update query
			foreach ($columns as $column) {
				if ($this->isColumn($column)) {
					$value_of[$column] = $column.'='.self::getConnection($con_type)->quote($this->$column);
				}
			}

			unset($value_of[$key]);

			$sql = 'UPDATE '.self::tableNameFromClassName(get_class($this)).' SET '
			. implode(', ', $value_of).' WHERE '.$key.' = '.self::getConnection($con_type)->quote($this->{$key});
			//Flash::set('error', $sql);
			$return = self::getConnection($con_type)->exec($sql) !== false;

			if ( ! $this->afterUpdate()) return false;
		}

		self::logQuery($sql,$con_type);

		Benchmark::cp($sql.':'.$con_type);

		// Run it !!...
		return $return;
	}

	private function isColumn($key)
	{
		// check if it is defined as column
		if(isset(self::$cols))
		{
			return (isset($this->$key) && self::$cols[$key]);
		}
		else
		{
			return isset($this->$key);
		}
	}

	/**
	 * Generates a delete string and execute it
	 *
	 * @param string $table the table name
	 * @param string $where the query condition
	 * @return boolean
	 */
	public function delete($key='id',$con_type='master')
	{
		Benchmark::cp();
		 
		if ( ! $this->beforeDelete()) return false;

		$sql = 'DELETE FROM '.self::tableNameFromClassName(get_class($this))
		. ' WHERE '.$key.'='.self::getConnection($con_type)->quote($this->{$key});

		// Run it !!...
		$return = self::getConnection($con_type)->exec($sql) !== false;
		if ($return && !$this->afterDelete()) {
			$this->save($key,$con_type);
			return false;
		}

		self::logQuery($sql,$con_type);
		Benchmark::cp($sql.':'.$con_type);

		return $return;
	}

	public function beforeSave() { return true; }
	public function beforeInsert() { return true; }
	public function beforeUpdate() { return true; }
	public function beforeDelete() { return true; }
	public function afterSave() { return true; }
	public function afterInsert() { return true; }
	public function afterUpdate() { return true; }
	public function afterDelete() { return true; }

	/**
	 * return a array of all columns in the table
	 * it is a good idea to rewrite this method in all your model classes
	 * used in save() for creating the insert and/or update sql query
	 */
	public function getColumns()
	{
		if(isset($this->cols))
		{
			$cols = $this->cols;
		}
		else
		{
			$cols = get_object_vars($this);
		}
		return array_keys($cols);
	}

	public static function filterCols($data,$fields)
	{
		if(!is_array($fields))
		{
			$fields = explode(',',$fields);
		}
		
		foreach($fields as $field)
		{
			if(isset($data[$field]))
			{
				$return[$field] = $data[$field];
			}
		}
		return $return;
	}


	public function setColumns($cols)
	{
		$this->cols = $cols;
	}


	public static function insert($class_name, $data,$con_type='master')
	{
		Benchmark::cp();
		 
		$keys = array();
		$values = array();

		foreach ($data as $key => $value) {
			$keys[] = $key;
			$values[] = self::getConnection($con_type)->quote($value);
		}

		$sql = 'INSERT INTO '.self::tableNameFromClassName($class_name).' ('.join(', ', $keys).') VALUES ('.join(', ', $values).')';

		self::logQuery($sql,$con_type);

		// Run it !!...
		$return = self::getConnection($con_type)->exec($sql) !== false;

		Benchmark::cp($sql.':'.$con_type.':'.implode(',',$values));

		return $return;
	}

	public static function update($class_name, $data, $where, $values=array(),$con_type='master')
	{
		Benchmark::cp();
		 
		$setters = array();

		// prepare request by binding keys
		foreach ($data as $key => $value) {
			$setters[] = $key.'='.self::getConnection($con_type)->quote($value);
		}

		$sql = 'UPDATE '.self::tableNameFromClassName($class_name).' SET '.join(', ', $setters).' WHERE '.$where;

		self::logQuery($sql,$con_type);

		$stmt = self::getConnection($con_type)->prepare($sql);
		$return = $stmt->execute($values);

		Benchmark::cp($sql.':'.$con_type.':'.implode(',',$values));

		return $return;
	}

	/**
	 * Update number by increasing or decreasing by number
	 *
	 * @param $class_name
	 * @param $data array('x'=>-1,'y'=>+3,...)
	 * @param $where
	 * @param $values
	 * @param $con_type
	 * @return unknown_type
	 */
	public static function increaseWhere($class_name, $data, $where, $values=array(),$con_type='master')
	{
		Benchmark::cp();
		 
		$setters = array();

		// prepare request by binding keys
		foreach ($data as $key => $value) {
			$setters[] = $key.'='.$key.' '.$value;
		}

		$sql = 'UPDATE '.self::tableNameFromClassName($class_name).' SET '.join(', ', $setters).' WHERE '.$where;

		self::logQuery($sql,$con_type);

		$stmt = self::getConnection($con_type)->prepare($sql);
		$return = $stmt->execute($values);

		Benchmark::cp($sql.':'.$con_type.':'.implode(',',$values));

		return $return;
	}

	public static function deleteWhere($class_name, $where, $values=array(),$con_type='master')
	{
		Benchmark::cp();
		 
		$sql = 'DELETE FROM '.self::tableNameFromClassName($class_name).' WHERE '.$where;

		self::logQuery($sql,$con_type);

		$stmt = self::getConnection($con_type)->prepare($sql);
		$return = $stmt->execute($values);

		Benchmark::cp($sql.':'.$con_type.':'.implode(',',$values));

		return $return;
	}
	 

	//
	// note: lazy finder or getter methode. Pratical when you need something really
	//       simple no join or anything will only generate simple select * from table ...
	//

	public static function findByIdFrom($class_name, $id,$key='id',$con_type='master')
	{
		return self::findOneFrom($class_name, $key.'=?', array($id),$con_type);
	}

	public static function findOneFrom($class_name, $where, $values=array(),$con_type='master',$fields='*')
	{
		Benchmark::cp();
		 
		// always read
		$con_type = 'READ_'.$con_type;
		 
		$sql = 'SELECT '.$fields.' FROM '.self::tableNameFromClassName($class_name).' WHERE '.$where.' LIMIT 0,1';

		$stmt = self::getConnection($con_type)->prepare($sql);
		$stmt->execute($values);


		self::logQuery($sql,$con_type);
		Benchmark::cp($sql.':'.$con_type.':'.implode(',',$values));

		return $stmt->fetchObject(self::classNameFromClassName($class_name));
	}

	public static function findAllFrom($class_name, $where=false, $values=array(),$con_type='master',$fields='*')
	{
		Benchmark::cp();
		 
		// always read
		$con_type = 'READ_'.$con_type;
		 
		$sql = 'SELECT '.$fields.' FROM '.self::tableNameFromClassName($class_name).($where ? ' WHERE '.$where:'');

		$stmt = self::getConnection($con_type)->prepare($sql);
		$stmt->execute($values);

		self::logQuery($sql,$con_type);
		Benchmark::cp($sql.':'.$con_type.':'.implode(',',$values));


		$objects = array();
		while ($object = $stmt->fetchObject(self::classNameFromClassName($class_name)))
		$objects[] = $object;

		return $objects;
	}

	public static function countFrom($class_name, $where=false, $values=array(),$con_type='master')
	{
		Benchmark::cp();
		 
		// always read
		$con_type = 'READ_'.$con_type;
		 
		$sql = 'SELECT COUNT(*) AS nb_rows FROM '.self::tableNameFromClassName($class_name).($where ? ' WHERE '.$where:'');

		$stmt = self::getConnection($con_type)->prepare($sql);
		$stmt->execute($values);

		self::logQuery($sql,$con_type);
		Benchmark::cp($sql.':'.$con_type.':'.implode(',',$values));

		return (int) $stmt->fetchColumn();
	}

	public static function quote($string,$con_type='master')
	{
		return self::getConnection($con_type)->quote($string);
	}
	
	/**
	 * append other object by given id 
	 * update: added multi result, extra  query, clean
	 * 
	 * @param array $records
	 * @param string $field get id from this field
	 * @param string $class_name object that is added
	 * @param string $class_field id field name for searched object
	 * @param string $alt_name name of property that will be appended
	 * @param string $con_type
	 * @param bool $clean  to clean appended objects to stdClass
	 * @param bool $multi to return multiple results as array
	 * @param string $sql_extra extra query string
	 */
	public static function appendObject($records,$field,$class_name,$class_field='id',$alt_name='',$con_type='master',$fields='*',$clean=false,$multi=false,$sql_extra='')
	{
		if(!is_array($records))
		{
			$records = array($records);
		}
		
		// get ids from records
		$ids = array();
		foreach($records as $r)
		{
			$ids[] = $r->{$field};
		}
	
		$ids = array_unique($ids);
		
		if(!$ids)
		{
			return false;
		}
		
		// add quoted values
		foreach($ids as $id)
		{
			$ids_[] = self::quote($id,$con_type);
		}

		$objects = self::findAllFrom($class_name,$sql_extra.' '.$class_field.' IN ('.implode(',',$ids_).')',array(),$con_type,$fields);
		
		if(!$objects)
		{
			return false;
		}
		
		foreach($objects as $o)
		{
			if($clean)
			{
				$o = self::cleanObject($o);
			}
			if($multi)
			{
				$objects_arr[$o->{$class_field}][] = $o;
			}
			else
			{
				$objects_arr[$o->{$class_field}] = $o;
			}
		}
		
		if(!$alt_name)
		{
			$alt_name = $class_name;
		}

		// assign retrieved objects 
		foreach($records as $r)
		{
			$r->{$alt_name} = $objects_arr[$r->{$field}];
		}
	}

	public static function cleanObject($obj)
	{
		
		if(is_array($obj))
		{
			foreach($obj as $k=>$o)
			{
				unset($o->cols);				
				$new_obj[$k] =  (object)get_object_vars($o);
			}			
		}
		else
		{
			unset($obj->cols);
			$new_obj = (object)get_object_vars($obj);
			
			
		}		
		return $new_obj;
	}
}

/**
 * The template object takes a valid path to a template file as the only argument
 * in the constructor. You can then assign properties to the template, which
 * become available as local variables in the template file. You can then call
 * display() to get the output of the template, or just call print on the template
 * directly thanks to PHP 5's __toString magic method.
 *
 * echo new View('my_template',array(
 *  'title' => 'My Title',
 *  'body' => 'My body content'
 * ));
 *
 * my_template.php might look like this:
 *
 * <html>
 * <head>
 *  <title><?php echo $title;?></title>
 * </head>
 * <body>
 *  <h1><?php echo $title;?></h1>
 *  <p><?php echo $body;?></p>
 * </body>
 * </html>
 *
 * Using view helpers:
 *
 * use_helper('HelperName', 'OtherHelperName');
 */
class View
{
	private $file;           // String of template file
	private $vars = array(); // Array of template variables
	static private $snippets = array(); // Array of snippet filenames

	/**
	 * Assign the template path
	 *
	 * @param string $file Template path (absolute path or path relative to the templates dir)
	 * @return void
	 */
	public function __construct($file, $vars=false)
	{
		 
		$this->file = APP_PATH.'/views/'.ltrim($file, '/').'.php';

		// check if view is in spec. language
		if(isset($vars['VIEW_LNG']))
		{
			// check for language spc. view
			$file = APP_PATH.'/views/'.ltrim($file, '/').'-'.$vars['VIEW_LNG'].'.php';
			if ( file_exists($file)) {
				$this->file = $file;
			}
		}
		
		// check if there template page for this view 
		$template = Config::option('template');
		if($template)
		{
			// check for language spc. view
			$file = CORE_ROOT.'/../user-content/templates/'.$template.'/views/'.ltrim($file, '/').'.php';
			if ( file_exists($file)) {
				$this->file = $file;
			}
		}
		

		if ( ! file_exists($this->file)) {
			throw new Exception("View '{$this->file}' not found!");
		}

		if ($vars !== false) {
			$this->vars = $vars;
		}
	}

	/**
	 * Assign specific variable to the template
	 *
	 * @param mixed $name Variable name
	 * @param mixed $value Variable value
	 * @return void
	 */
	public function assign($name, $value=null)
	{
		if (is_array($name)) {
			array_merge($this->vars, $name);
		} else {
			$this->vars[$name] = $value;
		}
	} // assign

	/**
	 * Display template and return output as string
	 *
	 * @return string content of compiled view template
	 */
	public function render()
	{
		ob_start();

		extract($this->vars, EXTR_SKIP);
		include $this->file;

		$content = ob_get_clean();
		return $content;
	}
	
	/**
	 * Render given file as snippet. Used to render same code several times in a loop in different files
	 *
	 * @param string $file name
	 * @param array $vars of variables tu be used in snippet
	 * @return string content of compiled view template
	 */
	static public function renderAsSnippet($file,$vars=false)
	{
		$_file = APP_PATH.'/views/'.ltrim($file, '/').'.php';
		
		// check if snippet loaded before
		if(!isset(self::$snippets['file'][$file]))
		{
			// check if file exists
			if ( ! file_exists($_file)) {
				throw new Exception("View '{$this->file}' not found!");
			}
			
			self::$snippets['file'][$file] = file_get_contents($_file);
		}
		
		ob_start();
		if($vars)
		{
			extract($vars, EXTR_SKIP);
		}
		eval('?>'.self::$snippets['file'][$file]);
		$content = ob_get_clean();
		
		return $content;
	}

	/**
	 * Display the rendered template
	 */
	public function display() { echo $this->render(); }

	/**
	 * Render the content and return it
	 * ex: echo new View('blog', array('title' => 'My title'));
	 *
	 * @return string content of the view
	 */
	public function __toString() { return $this->render(); }


	public function validation()
	{
		return Validation::getInstance();
	}

	public function input()
	{
		return Input::getInstance();
	}

	static public function escape($var)
	{
		$_escape = 'htmlspecialchars';
		$_encoding = 'UTF-8';
		 
		if (in_array($_escape, array('htmlspecialchars', 'htmlentities'))) {
			return call_user_func($_escape, $var, ENT_COMPAT, $_encoding);
		}

		return call_user_func($_escape, $var);
	}

} // end View class


/**
 * The Controller class should be the parent class of all of your Controller sub classes
 * that contain the business logic of your application (render a blog post, log a user in,
 * delete something and redirect, etc).
 *
 * In the Frog class you can define what urls / routes map to what Controllers and
 * methods. Each method can either:
 *
 * - return a string response
 * - redirect to another method
 */
class Controller
{
	protected $layout = false;
	protected $layout_vars = array();

	public function execute($action, $params)
	{
		// it's a private method of the class or action is not a method of the class
		if (substr($action, 0, 1) == '_' || ! method_exists($this, $action)) {
			throw new Exception("Action '{$action}' is not valid!");
		}
		call_user_func_array(array($this, $action), $params);
	}

	public function setLayout($layout)
	{
		$this->layout = $layout;
	}

	
	/**
	 * Set page meta content 
	 * @param $type
	 * @param $str
	 * @return unknown_type
	 */
	function setMeta($type='javascript',$str)
	{
		$this->layout_vars['meta'];
		
		if(!$this->layout_vars['meta'])
		{
			$this->layout_vars['meta'] = new stdClass();
		}
		
		switch($type)
		{
			case 'css':
				$this->layout_vars['meta']->css[$str] = '<link href="'.$str.'" rel="stylesheet" type="text/css" />';
				break;
			case 'javascript':
				$this->layout_vars['meta']->javascript[$str] = '<script type="text/javascript" src="'.$str.'"></script>';
				break;
			default:
				$this->layout_vars['meta']->{$type} = $str;
		}
	}

	public function assignToLayout($var, $value='')
	{
		if ( is_array($var) ) {
			$this->layout_vars = array_merge($this->layout_vars, $var);
		} else {
			$this->layout_vars[$var] = $value;
		}
	}

	public function render($view, $vars=array())
	{
		// merge with predefined vars
		$this->assignToLayout($vars);
		
		if ($this->layout)
		{
			// render view and layout
			$this->layout_vars['content_for_layout'] = new View($view, $this->layout_vars);
			return new View('../layouts/'.$this->layout, $this->layout_vars);
		}
		else
		{
			// render view 
			return new View($view, $this->layout_vars);
		}
	}

	public function display($view, $vars=array(), $exit=true)
	{
		echo $this->render($view, $vars);

		if ($exit){
			exit;
		}
	}

	public function renderJSON($data_to_encode)
	{
		if (class_exists('JSON')) {
			return JSON::encode($data_to_encode);
		} else if (function_exists('json_encode')) {
			return json_encode($data_to_encode);
		} else {
			throw new Exception('No function or class found to render JSON.');
		}
	}

	 

	public function validation()
	{
		$return = Validation::getInstance();
		$return->set_error_delimiters('<p class="msg-error-line">','</p>');
		return $return;
	}


	public function input()
	{
		return Input::getInstance();
	}
} // end Controller class

final class Observer
{
	static protected $events = array();

	public static function observe($event_name, $callback)
	{
		if ( ! isset(self::$events[$event_name]))
		self::$events[$event_name] = array();

		self::$events[$event_name][$callback] = $callback;
	}

	public static function stopObserving($event_name, $callback)
	{
		if (isset(self::$events[$event_name][$callback]))
		unset(self::$events[$event_name][$callback]);
	}

	public static function clearObservers($event_name)
	{
		self::$events[$event_name] = array();
	}

	public static function getObserverList($event_name)
	{
		return (isset(self::$events[$event_name])) ? self::$events[$event_name] : array();
	}

	/**
	 * If your event does not need to process the return values from any observers use this instead of getObserverList()
	 */
	public static function notify($event_name)
	{
		$args = array_slice(func_get_args(), 1); // removing event name from the arguments

		foreach(self::getObserverList($event_name) as $callback)
		call_user_func_array($callback, $args);
	}
}

/**
 * The AutoLoader class is an object oriented hook into PHP's __autoload functionality. You can add
 *
 * - Single Files AutoLoader::addFile('Blog','/path/to/Blog.php');
 * - Multiple Files AutoLoader::addFile(array('Blog'=>'/path/to/Blog.php','Post'=>'/path/to/Post.php'));
 * - Whole Folders AutoLoader::addFolder('path');
 *
 * When adding a whole folder each file should contain one class named the same as the file without ".php" (Blog => Blog.php)
 */
class AutoLoader
{
	protected static $files = array();
	protected static $folders = array();

	/**
	 * AutoLoader::addFile('Blog','/path/to/Blog.php');
	 * AutoLoader::addFile(array('Blog'=>'/path/to/Blog.php','Post'=>'/path/to/Post.php'));
	 * @param mixed $class_name string class name, or array of class name => file path pairs.
	 * @param mixed $file Full path to the file that contains $class_name.
	 */
	public static function addFile($class_name, $file=null)
	{
		if ($file == null && is_array($class_name)) {
			self::$files = array_merge(self::$files, $class_name);
		} else {
			self::$files[$class_name] = $file;
		}
	}

	/**
	 * AutoLoader::addFolder('/path/to/my_classes/');
	 * AutoLoader::addFolder(array('/path/to/my_classes/','/more_classes/over/here/'));
	 * @param mixed $folder string, full path to a folder containing class files, or array of paths.
	 */
	public static function addFolder($folder)
	{
		if ( ! is_array($folder)) {
			$folder = array($folder);
		}
		self::$folders = array_merge(self::$folders, $folder);
	}

	public static function load($class_name)
	{
		if (isset(self::$files[$class_name])) {
			if (file_exists(self::$files[$class_name])) {
				require self::$files[$class_name];
				return;
			}
		} else {
			foreach (self::$folders as $folder) {
				$folder = rtrim($folder, DIRECTORY_SEPARATOR);
				$file = $folder.DIRECTORY_SEPARATOR.$class_name.'.php';
				if (file_exists($file)) {
					require $file;
					return;
				}
			}
		}
		throw new Exception("AutoLoader did not found file for '{$class_name}'!");
	}

} // end AutoLoader class

if ( ! function_exists('__autoload')) {
	AutoLoader::addFolder(array(APP_PATH.DIRECTORY_SEPARATOR.'models',
	APP_PATH.DIRECTORY_SEPARATOR.'controllers',
	HELPER_PATH));
	function __autoload($class_name)
	{
		AutoLoader::load($class_name);
	}
}

/**
 * Flash service
 *
 * Purpose of this service is to make some data available across pages. Flash
 * data is available on the next page but deleted when execution reach its end.
 *
 * Usual use of Flash is to make possible that current page pass some data
 * to the next one (for instance success or error message before HTTP redirect).
 *
 * Flash::set('errors', 'Blog not found!');
 * Flass::set('success', 'Blog have been saved with success!');
 * Flash::get('success');
 *
 * You can only set 20 cookies per domain
 *
 * Flash service as a concep is taken from Rails. This thing is really useful!
 */
final class Flash
{
	const COOKIE_KEY = 'framework_flash_';
	const COOKIE_LIFE = 86400; // 1 day

	private static $_previous = array(); // Data that prevous page left in the Flash

	/**
	 * Return specific variable from the flash. If value is not found NULL is
	 * returned
	 *
	 * @param string $var Variable name
	 * @return mixed
	 */
	public static function get($var)
	{
		return isset(self::$_previous[self::COOKIE_KEY.$var]) ? base64_decode(self::$_previous[self::COOKIE_KEY.$var]) : null;
	}

	/**
	 * Add specific variable to the flash. This variable will be available on the
	 * next page unlease removed with the removeVariable() or clear() method
	 *
	 * @param string $var Variable name
	 * @param mixed $value Variable value
	 * @return void
	 */
	public static function set($var, $value)
	{
		$time = $_SERVER['REQUEST_TIME'] + self::COOKIE_LIFE;
		self::setCookie(self::COOKIE_KEY.$var, $value, $time);
	} // set

	/**
	 * Call this function to clear flash. Note that data that previous page
	 * stored will not be deleted - just the data that this page saved for
	 * the next page
	 *
	 * @param none
	 * @return void
	 */
	public static function clear()
	{
		if ( ! empty($_COOKIE) && is_array($_COOKIE))
		{
			foreach($_COOKIE as $key=>$val)
			{
				if(strpos($key,self::COOKIE_KEY)!==false)
				{
					self::clearCookie($key);
				}
			}
		}
	} // clear

	/**
	 * This function will read flash data from the $_SESSION variable
	 * and load it into $this->previous array
	 *
	 * @param none
	 * @return void
	 */
	public static function init()
	{
		// Get flash data...
		if ( ! empty($_COOKIE) && is_array($_COOKIE))
		{
			self::$_previous = $_COOKIE;
		}
		self::clear();
	}

	public static function getCookie($name)
	{
		return base64_decode($_COOKIE[$name]);
	}

	public static function clearCookie($name)
	{
		$time = $_SERVER['REQUEST_TIME'] - self::COOKIE_LIFE;
		self::setCookie($name,false,$time);
	}

	/**
	 * sets cookie. value base64 encoded.
	 *
	 * @param string $name
	 * @param string $value
	 * @param int $time 0: expires when browser closed, or time
	 * @param string $domain use NULL to set for current domain, '' for all domains (general), or any specific domain
	 */
	public static function setCookie($name,$value,$time,$domain='')
	{
		$value = base64_encode($value);
		 
		if($domain === '')
		{
			$domain = COOKIE_DOMAIN;
		}
		 
		setcookie($name, $value, $time, '/', $domain, (isset($_ENV['SERVER_PROTOCOL']) && (strpos($_ENV['SERVER_PROTOCOL'],'https') || strpos($_ENV['SERVER_PROTOCOL'],'HTTPS'))));
	}

} // end Flash class


final class Inflector
{
	/**
	 *  Return an CamelizeSyntaxed (LikeThisDearReader) from something like_this_dear_reader.
	 *
	 * @param string $string Word to camelize
	 * @return string Camelized word. LikeThis.
	 */
	public static function camelize($string)
	{
		return str_replace(' ','',ucwords(str_replace('_',' ', $string)));
	}

	/**
	 * Return an underscore_syntaxed (like_this_dear_reader) from something LikeThisDearReader.
	 *
	 * @param  string $string CamelCased word to be "underscorized"
	 * @return string Underscored version of the $string
	 */
	public static function underscore($string)
	{
		return strtolower(preg_replace('/(?<=\\w)([A-Z])/', '_\\1', $string));
	}

	/**
	 * Return an Humanized syntaxed (Like this dear reader) from something like_this_dear_reader.
	 *
	 * @param  string $string CamelCased word to be "underscorized"
	 * @return string Underscored version of the $string
	 */
	public static function humanize($string)
	{
		return ucfirst(str_replace('_', ' ', $string));
	}

	/**
	 * return gogle friendly readible url
	 *
	 * @param  string $string
	 * @return string $string
	 */
	public static function slugify($str)
	{
		$str = preg_replace("/[^a-zA-Z0-9- ]/", "", $str);
		$str = strtolower(str_replace(" ", "-", trim($str)));
		return $str;
	}

	public static function utf8Substr($str,$from,$len)
	{
		# utf8 substr
		# www.yeap.lv
		return preg_replace('#^(?:[\x00-\x7F]|[\xC0-\xFF][\x80-\xBF]+){0,'.$from.'}'.
		'((?:[\x00-\x7F]|[\xC0-\xFF][\x80-\xBF]+){0,'.$len.'}).*#s',
		'$1',$str);
	}
}

// ----------------------------------------------------------------
//   global function
// ----------------------------------------------------------------

/**
 * Load all functions from the helper file
 *
 * syntax:
 * use_helper('Cookie');
 * use_helper('Number', 'Javascript', 'Cookie', ...);
 *
 * @param  string helpers in CamelCase
 * @return void
 */
function use_helper()
{
	static $_helpers = array();

	$helpers = func_get_args();

	foreach ($helpers as $helper) {
		if (in_array($helper, $_helpers)) continue;

		$helper_file = HELPER_PATH.DIRECTORY_SEPARATOR.$helper.'.php';

		if ( ! file_exists($helper_file)) {
			throw new Exception("Helper file '{$helper}' not found!");
		}

		include $helper_file;
		$_helpers[] = $helper;
	}
}

/**
 * Load model class from the model file (faster then waiting for the __autoload function)
 *
 * syntax:
 * use_model('Blog');
 * use_model('Post', 'Category', 'Tag', ...);
 *
 * @param  string models in CamelCase
 * @return void
 */
function use_model()
{
	static $_models = array();

	$models = func_get_args();

	foreach ($models as $model) {
		if (in_array($model, $_models)) continue;

		$model_file = APP_PATH.DIRECTORY_SEPARATOR.'models'.DIRECTORY_SEPARATOR.$model.'.php';

		if ( ! file_exists($model_file)) {
			throw new Exception("Model file '{$model}' not found!");
		}

		include $model_file;
		$_models[] = $model;
	}
}


/**
 * Load file cnotents once
 *
 * syntax:
 * use_file('Zend/Cache.php',...);
 *
 * @param  string files
 * @return void
 */
function use_file()
{
	static $_files = array();

	$files = func_get_args();

	foreach ($files as $file) {
		if ($_files[$file]) continue;

		/*if ( ! file_exists($file)) {
		 throw new Exception("File '{$file}' not found!");
		 }*/

		include $file;
		$_files[$file] = 1;
	}
}


/**
 * create a real nice url like http://www.example.com/controller/action/params#anchor
 *
 * you can put many params as you want,
 * if a params start with # it is considerated a Anchor
 *
 * get_url('controller/action/param1/param2') // I always use this method
 * get_url('controller', 'action', 'param1', 'param2');
 *
 * @param string conrtoller, action, param and/or #anchor
 * @return string
 */
function get_url()
{
	$base_url = BASE_URL;

	$params = func_get_args();
	if (count($params) === 1) return $base_url . $params[0];

	$url = '';
	foreach ($params as $param) {
		if (strlen($param)) {
			$url .= $param{0} == '#' ? $param: '/'. $param;
		}
	}
	return $base_url . preg_replace('/^\/(.*)$/', '$1', $url);
}

/**
 * Get the request method used to send this page
 *
 * @return string possible value: GET, POST or AJAX
 */
function get_request_method()
{
	if (isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest') return 'AJAX';
	else if ( ! empty($_POST)) return 'POST';
	else return 'GET';
}

/**
 * Redirect this page to the url passed in param
 */
function redirect($url,$permanent=false)
{
	if($permanent)
	{
		// Permanent redirection
		header("HTTP/1.1 301 Moved Permanently");
	}

	header('Location: '.$url); exit;
}

/**
 * Alias for redirect
 */
function redirect_to($url)
{
	header('Location: '.$url); exit;
}

/**
 * Encodes HTML safely for UTF-8. Use instead of htmlentities.
 */
function html_encode($string)
{
	return htmlentities($string, ENT_QUOTES, 'UTF-8') ;
}

/**
 * Display a 404 page not found and exit
 */
function page_not_found()
{
	header_404();
	echo new View('404');
	exit;
}

function header_404()
{
	ob_start();
	header('HTTP/1.0 404 Not Found');
}

function header_200()
{
	ob_start();
	header('HTTP/1.1 200 OK');
}

function header_503()
{
	header('HTTP/1.1 503 Service Temporarily Unavailable');
	header('Status: 503 Service Temporarily Unavailable');
	header('Retry-After: 7200');
	header('X-Powered-By:');
}


function page_503()
{
	ob_start();
	header_503();
	?>
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html>
<head>
<title>503 Service Temporarily Unavailable</title>
</head>
<body>
<div style="text-align: center; margin: 40px;">

<h2>503 Service Temporarily Unavailable</h2>


</div>
</body>
</html>
	<?php
	exit;
}

function convert_size($num)
{
	if ($num >= 1073741824) $num = round($num / 1073741824 * 100) / 100 .' gb';
	else if ($num >= 1048576) $num = round($num / 1048576 * 100) / 100 .' mb';
	else if ($num >= 1024) $num = round($num / 1024 * 100) / 100 .' kb';
	else $num .= ' b';
	return $num;
}

// information about time and memory

function memory_usage()
{
	return convert_size(memory_get_usage());
}

function execution_time()
{
	return sprintf("%01.4f", get_microtime() - FRAMEWORK_STARTING_MICROTIME);
}

function get_microtime()
{
	$time = explode(' ', microtime());
	return doubleval($time[0]) + $time[1];
}

function odd_even()
{
	static $odd = true;
	return ($odd = !$odd) ? 'even': 'odd';
}

function even_odd()
{
	return odd_even();
}

/**
 * Provides a nice print out of the stack trace when an exception is thrown.
 *
 * @param Exception $e Exception object.
 */
function framework_exception_handler($e)
{
	if ( ! DEBUG) page_not_found();

	header_404();

	echo '<style>h1,h2,h3,p,td {font-family:Verdana; font-weight:lighter;}</style>';
	echo '<p>Uncaught '.get_class($e).'</p>';
	echo '<h1>'.$e->getMessage().'</h1>';

	$traces = $e->getTrace();
	if (count($traces) > 1) {
		echo '<p><b>Trace in execution order:</b></p>'.
             '<pre style="font-family:Verdana; line-height: 20px">';

		$level = 0;
		foreach (array_reverse($traces) as $trace) {
			++$level;

			if (isset($trace['class'])) echo $trace['class'].'&rarr;';

			$args = array();
			if ( ! empty($trace['args'])) {
				foreach ($trace['args'] as $arg) {
					if (is_null($arg)) $args[] = 'null';
					else if (is_array($arg)) $args[] = 'array['.sizeof($arg).']';
					else if (is_object($arg)) $args[] = get_class($arg).' Object';
					else if (is_bool($arg)) $args[] = $arg ? 'true' : 'false';
					else if (is_int($arg)) $args[] = $arg;
					else {
						$arg = htmlspecialchars(substr($arg, 0, 64));
						if (strlen($arg) >= 64) $arg .= '...';
						$args[] = "'". $arg ."'";
					}
				}
			}
			echo '<b>'.$trace['function'].'</b>('.implode(', ',$args).')  ';
			echo 'on line <code>'.(isset($trace['line']) ? $trace['line'] : 'unknown').'</code> ';
			echo 'in <code>'.(isset($trace['file']) ? $trace['file'] : 'unknown')."</code>\n";
			echo str_repeat("   ", $level);
		}
		echo '</pre>';
	}
	echo "<p>Exception was thrown on line <code>"
	. $e->getLine() . "</code> in <code>"
	. $e->getFile() . "</code></p>";

	$dispatcher_status = Dispatcher::getStatus();
	$dispatcher_status['request method'] = get_request_method();
	debug_table($dispatcher_status, 'Dispatcher status');
	if ( ! empty($_GET)) debug_table($_GET, 'GET');
	if ( ! empty($_POST)) debug_table($_POST, 'POST');
	if ( ! empty($_COOKIE)) debug_table($_COOKIE, 'COOKIE');
	debug_table($_SERVER, 'SERVER');
}

function debug_table($array, $label, $key_label='Variable', $value_label='Value')
{
	echo '<h2>'.$label.'</h2>';
	echo '<table cellpadding="3" cellspacing="0" style="width: 800px; border: 1px solid #ccc">';
	echo '<tr><td style="border-right: 1px solid #ccc; border-bottom: 1px solid #ccc;">'.$key_label.'</td>'.
         '<td style="border-bottom: 1px solid #ccc;">'.$value_label.'</td></tr>';

	foreach ($array as $key => $value) {
		if (is_null($value)) $value = 'null';
		else if (is_array($value)) $value = 'array['.sizeof($value).']';
		else if (is_object($value)) $value = get_class($value).' Object';
		else if (is_bool($value)) $value = $value ? 'true' : 'false';
		else if (is_int($value)) $value = $value;
		else {
			$value = htmlspecialchars(substr($value, 0, 64));
			if (strlen($value) >= 64) $value .= ' &hellip;';
		}

		echo '<tr><td><code>'.htmlspecialchars($key).'</code></td><td><code>'.$value.'</code></td></tr>';
	}
	echo '</table>';
}

function debug_dump($array, $label, $key_label='Variable', $value_label='Value')
{
	echo '<h2>'.$label.'</h2>';
	echo '<pre>';

	foreach ($array as $key => $value) {
		echo '<tr><td><code> '.$key.' </code></td><td><code> ';
		 
		if (is_null($value)) echo 'null';
		else if (is_array($value)) var_dump($value);
		else if (is_object($value)) var_dump($value);
		else if (is_bool($value)) echo $value ? 'true' : 'false';
		else if (is_int($value)) echo $value;
		else {
			$value = htmlspecialchars(substr($value, 0, 64));
			if (strlen($value) >= 64) $value .= ' &hellip;';
			echo $value;
		}
		echo ' </code></td></tr>';
	}
	echo '</pre>';
}

set_exception_handler('framework_exception_handler');

/**
 * This function will strip slashes if magic quotes is enabled so
 * all input data ($_GET, $_POST, $_COOKIE) is free of slashes
 */
function fix_input_quotes()
{
	$in = array(&$_GET, &$_POST, &$_COOKIE);
	while (list($k,$v) = each($in)) {
		foreach ($v as $key => $val) {
			if (!is_array($val)) {
				$in[$k][$key] = stripslashes($val); continue;
			}
			$in[] =& $in[$k][$key];
		}
	}
	unset($in);
} // fix_input_quotes

if (PHP_VERSION < 6 && get_magic_quotes_gpc()) {
	fix_input_quotes();
}

/* general functions*/

function ife($condition,$true,$false='')
{
	if($condition)
	{
		return $true;
	}
	else
	{
		return $false;
	}
}


// benchmark
Benchmark::cp('START(framework end)');
//Benchmark::$active=true;
//Benchmark::setNoCache(true);


function display_benchmark()
{
	if(Benchmark::$active)
	{
		echo Benchmark::report();
		echo 'memory usage: <b>'.memory_usage().'</b><br/>';
		debug_dump(Record::$__QUERIES__,'__QUERIES__');
	}

	//AuthUser::isLoggedIn(false);
	//var_dump(AuthUser::$user);
}
register_shutdown_function  ( 'display_benchmark' );