<?php
/**
 * MiniMVC
 *
 * Convention-based micro-framework for PHP
 *
 * @author 		Timothy J. Warren
 * @copyright	Copyright (c) 2011 - 2012
 * @link 		https://github.com/timw4mail/miniMVC
 * @license 	http://philsturgeon.co.uk/code/dbad-license 
 */
 
// --------------------------------------------------------------------------
 
/**
 * Extend PHP's PDO class to add some more functionality
 *
 * @extends PDO
 */
class db extends PDO {

	private $statement;
	private static $instance = array();
	
	public static function &get_instance($dbname="default", $options=array())
	{
		if ( ! isset(self::$instance[$dbname]))
		{
			//echo 'Creating new instance of db class.';
			self::$instance[$dbname] = self::_get_conf($dbname, $options);
		}

		return self::$instance[$dbname];
	}
	
	// --------------------------------------------------------------------------
	
	/**
	 * Makes DSN from config file, and creates database object
	 *
	 * @param string $dbname
	 * @param array $options
	 * @return db object
	 */
	private static function _get_conf($dbname="default", $options=array())
	{
		// Include the database config file
		require(APP_PATH.'config/db.php');
		
		// Get the correct database in the config file
		if(is_like_array($db_conf[$dbname]))
		{
			// Array manipulation is too verbose
			extract($db_conf[$dbname]);
		}
		else
		{
			// Apparently the database doesn't exist
			$this->get_last_error();
			trigger_error("Database does not exist", E_USER_ERROR);
			die();
		}
		
		// Sqlite doesn't use dbname param
		$dsn = (stripos($type, "sqlite") === FALSE) ? "{$type}:dbname={$db}" : "{$type}:{$db}";
		
		// Set hostname if applicable
		if(isset($host))
		{
			$dsn .= ($host !== "") ? ";host={$host}" : "";
		}

		// Set port if applicable
		if(isset($port))
		{
			$dsn .= ($port !== "") ? ";port={$port}" : "";
		}
		
		$user = ( ! empty($user)) ? $user : null;
		$pass = ( ! empty($pass)) ? $pass : null;

		// Pre-set the error mode
		$opts = array(
			PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
		);
		
		$options = $opts + $options;
		
		return new db($dsn, $user, $pass, $options);
	}
	
	// --------------------------------------------------------------------------
	
	/**
	 * Constructor to override PDO constructor - Quercus doesn't seem to override
	 * the parent constructor unless the arguments match. 
	 *
	 * @param string $dsn
	 * @param string $user
	 * @param string $pass
	 * @param array $options
	 */
	function __construct($dsn, $user, $pass, $options)
	{
		// Let's try connecting now!
		parent::__construct($dsn, $user, $pass, $options);

	}
	
	// --------------------------------------------------------------------------
 	
 	/** 
 	 * PHP magic method to facilitate dynamic methods
 	 *
 	 * @param string $name
 	 * @param array $args
 	 */
 	function __call($name, $args)
 	{
 		if(is_callable($this->$name))
 		{
 			//Add $this to the beginning of the args array
 			array_unshift($args, $this);
 			
 			//Call the dynamic function
 			return call_user_func_array($this->$name, $args);
 		}
 	}
 	
 	// --------------------------------------------------------------------------
 	
 	/**
 	 * PHP magic methods to call non-static methods statically
 	 *
 	 * @param string $name
 	 * @param array $args
 	 */
 	public static function __callStatic($name, $args)
 	{
 		if(is_callable(parent::$name))
 		{
 			return call_user_func_array(parent::$name, $args);
 		}
 	}
	
	// --------------------------------------------------------------------------
	
	/**
	 * Prints out the contents of the object when used as a string
	 * 
	 * @return string
	 */
	function __toString()
	{
		$args = func_get_args();
		$method = ( ! empty($args)) ? $args[0] : "print_r";
		
		$output = '<pre>';
	
		if($method == "var_dump")
		{
			ob_start();
			var_dump($this);
			$output .= ob_get_contents();
			ob_end_clean();
		}
		else if($method == "var_export")
		{
			ob_start();
			var_export($this);
			$output .= ob_get_contents();
			ob_end_clean();
		}	
		else
		{
			$output .= print_r($this, TRUE);
		}
	
		return $output . '</pre>';
	}
	
	// --------------------------------------------------------------------------
	
	/**
	 * Simplifies prepared statements for database queries
	 *
	 * @param string $sql
	 * @param array $data
	 * @return mixed PDOStatement / FALSE
	 */
	function prepare_query($sql, $data)
	{
		// Prepare the sql
		$query = $this->prepare($sql);
		
		if( ! is_like_array($query))
		{
			$this->get_last_error();
			return FALSE;
		}
		
		// Set the statement in the class variable for easy later access
		$this->statement =& $query;
		
		
		if( ! is_like_array($data))
		{
			trigger_error("Invalid data argument");
			return FALSE;
		}
		
		// Bind the parameters
		foreach($data as $k => $value)
		{
			$res = $query->bindValue($k, $value);
			
			if( ! $res)
			{
				trigger_error("Parameter not successfully bound");
				return FALSE;
			}
		}
		
		return $query; 
		
	}
	
	// --------------------------------------------------------------------------
    
	/**
	 * Returns the last error from the database
	 *
	 * @return string
	 */
	function get_last_error()
	{
		$error = array();

		if(isset($this->statement))
		{
			$error = $this->statement->errorInfo();
		}
		else
		{
			$error = $this->errorInfo();
		}

		$code = $error[0];
		$driver_code = $error[1];
		$message = $error[2];

		// Contain the content for buffering
		ob_start();

		include(APP_PATH.'/errors/error_db.php');

		$buffer = ob_get_contents();
		ob_end_clean();
		echo $buffer;
	}
}

// End of db.php