<?php

/**
 * Smart Report Maker
 * Version 9.0.0
 * Author : Webuccino
 * All copyrights are preserved to Webuccino
 * URL : http://mysqlreports.com/
 *
 */

if (!defined("DIRECTACESS")) {
    exit("No direct script access allowed");
}

class PDOHandler
{

    protected $host,
    $user,
    $pass,
    $db,
    $link,
    $debug     = false,
    $numOfRows = '',
        $dashboard;
    protected $keyTypes = array("NUM", "ASSOC", "BOTH");

    public function __construct($host, $user, $pass, $db, $isDebug)
    {
        $this->is_debug($isDebug);
        $this->host = $host;
        $this->user = $user;
        $this->pass = $pass;
        $this->db   = $db;

        $this->link = $this->connect();
    }

    private function connect()
    {

        $this->debug_mode('PDO::connect', 'info', '#Attempt connection');
        try {
            try {
                $connection = new pdo('mysql:host=' . $this->host . ';dbname=' . $this->db, $this->user, $this->pass, array(PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8'));
            } catch (PDOException $ex) {
                $this->log("DB Connection Error" . $e->getMessage() . " Error code" . $e->getCode() . " Error Line" . $e->getLine(), "", "", $e->getTraceAsString());
                $connection = new pdo('mysql:host=' . $this->host . ';dbname=' . $this->db . ';charset=UTF-8', $this->user, $this->pass);
            }

            if ($this->debug) {

                $this->debug_mode('connect', 'info', "");
            }

        } catch (PDOException $e) {
            $this->log("DB Connection Error" . $e->getMessage() . " Error code" . $e->getCode() . " Error Line" . $e->getLine(), "", "", $e->getTraceAsString());
            $this->debug_mode('connect', 'error', '#connection failed: ' . $e->getMessage());

            return false;
        }

        $this->debug_mode('connect', 'success', '#connected successfully');
        return $connection;
    }

    // this function make query to fetch data from database ( Like using SELECT & SHOW ), this function return array and not handler
    public function query($sqlStatement, $keyType = "NUM", $params = array(), $dashboard = null)
    {

// $keyType = ASSOC, NUM, BOTH

        if (defined("TestMode")) {
            echo "<pre class='prettyprint'>$sqlStatement </pre>";
            echo "<h6>Query parameters:</h6>";
            var_dump($params);
        }

        if ($keyType === 'BOTH') {
            $keyType = PDO::FETCH_BOTH;
        }

        //PDO::FETCH_BOTH
        else

        if ($keyType === 'ASSOC') {
            $keyType = PDO::FETCH_ASSOC;
        }

        //PDO::FETCH_ASSOC
        else {
            $keyType = PDO::FETCH_NUM;
        }

        //PDO::FETCH_NUM

        $this->debug_mode('query', 'info', $sqlStatement);

        if ($this->link) {
            try {
                $this->link->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
                $this->link->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
                $query = $this->link->prepare($sqlStatement);

                if ($query && count($params) > 0) {
                    $result = $query->execute($params);
                } else {
                    $result = $query->execute();
                }

                if (!$result) {
                    $this->log(implode(",", $this->link->errorInfo()), $sqlStatement, $params);
                    $this->debug_mode('query', 'fail', '#Query failed' . implode(",", $this->link->errorInfo()));
                    return false;
                } else {
                    $this->numOfRows = $query->rowCount();

                    if (log_diagnostic_mode == "info") {
                        $this->log("", $sqlStatement, $params, "info", $query->rowCount());
                    }

                    $fetchedData = $query->fetchAll($keyType);

                    $query->closeCursor();
                    return $fetchedData;
                }

            } catch (PDOException $e) {
                $this->log($e->getMessage() . " Error code" . $e->getCode() . " Error Line" . $e->getLine(), $sqlStatement, $params, $e->getTraceAsString());
                $this->debug_mode('query', 'error', '#query failed: ' . $e->getMessage());
                return false;
            }

        }

    }

    public function sanitize_values($string)
    {
        $cleaned_string = $string;

        $this->debug_mode('sanitize_values', 'info', '#input string for the sanitize function : ' . $string);

        if ($this->link) {
            // $cleaned_string =  $this->link->quote($string);
            $cleaned_string = str_replace(array('\\', "\0", "\n", "\r", "'", '"', "\x1a"), array('\\\\', '\\0', '\\n', '\\r', "\\'", '\\"', '\\Z'), $cleaned_string);
            $cleaned_string = str_replace(array(" union", " delete", " select", " insert", "=", "\t", "*", " limit", " null", " table", "(", ")", " where", " update"), "", strtolower($cleaned_string));
            $this->debug_mode('sanitize_values', 'success', '#sanitized string : ' . $cleaned_string);
        }

        return $cleaned_string;
    }

    // this function display what's happened while object of this class start actions ( Just work when debug = true )
    private function debug_mode($functionName, $type, $msg)
    {

        if ($this->debug) {
            $color = "black";

// by default
            if ($type === "error") {
                $color = "red";
            }

            // error
            else

            if ($type === "success") {
                $color = "green";
            }

            // success
            else

            if ($type === "info") {
                $color = "blue";
            }

            // info

            debug("\n ## DbHandler -> PDO : " . $msg);
        }

    }

    // this function to check if connection failed or succeeded
    public function is_connection_failed()
    {

// if connection failed return true
        if ($this->link === false) {
            return true;
        } else {
            return false;
        }

    }

    // this function return number of rows for current query
    public function get_num_rows()
    {
        return $this->numOfRows;
    }

    // this function return database handler type
    public function get_db_handler_type()
    {
        return 'pdo';
    }

    // this function set debug mode
    public function is_debug($bool)
    {

        if ($bool !== false) {
            $bool = true;
        }

        $this->debug = $bool;
    }

    // this function for close connection
    public function close_connection()
    {

        if ($this->link) {
            $this->link = null;
        }

    }

    public function command($sqlStatement, $keyType = "NUM", $params = array(), $dashboard = null)
    {

// $keyType = ASSOC, NUM, BOTH
        if (defined("TestMode")) {
            echo "<pre class='prettyprint'>$sqlStatement </pre>";
            echo "<h6>Query parameters:</h6>";
            var_dump($params);
        }

        if ($keyType === 'BOTH') {
            $keyType = PDO::FETCH_BOTH;
        }

        //PDO::FETCH_BOTH
        else

        if ($keyType === 'ASSOC') {
            $keyType = PDO::FETCH_ASSOC;
        }

        //PDO::FETCH_ASSOC
        else {
            $keyType = PDO::FETCH_NUM;
        }

        //PDO::FETCH_NUM

        $this->debug_mode('query', 'info', $sqlStatement);

        if ($this->link) {
            try {
                $this->link->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
                $this->link->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
                $query = $this->link->prepare($sqlStatement);

                if ($query && count($params) > 0) {
                    $result = $query->execute($params);
                } else {
                    $result = $query->execute();
                }

                if (!$result) {
                    $this->log(implode(",", $this->link->errorInfo()), $sqlStatement, $params);
                    $this->debug_mode('query', 'fail', '#Query failed' . implode(",", $this->link->errorInfo()));
                    return false;
                } else {

                    if (stristr($sqlStatement, "insert ") && $this->link->lastInsertId() != "0") {

                        if (log_diagnostic_mode == "info") {
                            $this->log("", $sqlStatement, $params, "info", $query->rowCount());
                        }

                        return $this->link->lastInsertId();
                    } else {

                        if (log_diagnostic_mode == "info") {
                            $this->log("", $sqlStatement, $params, "info", $query->rowCount());
                        }

                        return $query->rowCount();
                    }

                }

            } catch (PDOException $e) {
                $this->log($e->getMessage() . " Error code" . $e->getCode() . " Error Line" . $e->getLine(), $sqlStatement, $params, $e->getTraceAsString());

                return false;
            }

        }

    }

    public function do_transaction($sqlStatement_array)
    {
        try {
            $this->link->beginTransaction();

            foreach ($sqlStatement_array as $sqlStatement) {
                $result = $this->command($sqlStatement["sql"], "NUM", $sqlStatement["params"]);

                if (!$result) {
                    $this->link->rollback();
                    // other clean-up goes here
                    return false;
                } else {
                    var_dump($result);
                }

            }

            $this->link->commit();
            return true;
        } catch (Exception $ex) {
            throw new Exception($ex->getMessage());
            $this->link->rollback();
            return false;
        }

    }

    private function log($error, $query, $params, $trace = "", $severity = "error", $result_count = "")
    {

        if ($severity == "error") {
            srm_dashboard::log($error, __FILE__, "", $trace, "error", "report_builder", $query, json_encode($params));
        } else {
            $params = array(
                "query_params"          => $params,
                "returned_result_count" => $result_count
            );
            srm_dashboard::log("", __FILE__, "", "", "info", "report_builder", $query, json_encode($params));
        }

    }

}
