detain/db_abstraction

View on GitHub
src/Pdo/Db.php

Summary

Maintainability
A
3 hrs
Test Coverage
<?php
/**
 * PDO SQL Related Functionality
 * @author Joe Huss <detain@interserver.net>
 * @copyright 2019
 * @package MyAdmin
 * @category SQL
 */

namespace MyDb\Pdo;

use MyDb\Generic;
use MyDb\Db_Interface;
use PDO;

/**
 * Db
 *
 * @access public
 */
class Db extends Generic implements Db_Interface
{
    /* public: connection parameters */
    public $driver = 'mysql';
    public $Rows = [];
    /* public: this is an api revision, not a CVS revision. */
    public $type = 'pdo';

    /**
     * changes the database we are working with.
     *
     * @param string $database the name of the database to use
     * @return void
     */
    public function selectDb($database)
    {
        $dSN = "{$this->driver}:dbname={$database};host={$this->host}";
        if ($this->characterSet != '') {
            $dSN .= ';charset='.$this->characterSet;
        }
        $this->linkId = new PDO($dSN, $this->user, $this->password);
        $this->database = $database;
    }


    /**
     * alias function of select_db, changes the database we are working with.
     *
     * @param string $database the name of the database to use
     * @return void
     */
    public function useDb($database)
    {
        $this->selectDb($database);
    }

    /* public: connection management */

    /**
     * Db::connect()
     * @param string $database
     * @param string $host
     * @param string $user
     * @param string $password
     * @param string $driver
     * @return bool|int|PDO
     */
    public function connect($database = '', $host = '', $user = '', $password = '', $driver = 'mysql')
    {
        /* Handle defaults */
        if ('' == $database) {
            $database = $this->database;
        }
        if ('' == $host) {
            $host = $this->host;
        }
        if ('' == $user) {
            $user = $this->user;
        }
        if ('' == $password) {
            $password = $this->password;
        }
        if ('' == $driver) {
            $driver = $this->driver;
        }
        /* establish connection, select database */
        $dSN = "{$driver}:dbname={$database};host={$host}";
        if ($this->characterSet != '') {
            $dSN .= ';charset='.$this->characterSet;
        }
        if ($this->linkId === false) {
            try {
                $this->linkId = new PDO($dSN, $user, $password);
            } catch (\PDOException $e) {
                $this->halt('Connection Failed '.$e->getMessage());
                return 0;
            }
        }
        return $this->linkId;
    }

    /* This only affects systems not using persistent connections */

    /**
     * Db::disconnect()
     * @return void
     */
    public function disconnect()
    {
    }

    /* public: discard the query result */

    /**
     * Db::free()
     * @return void
     */
    public function free()
    {
        //            @mysql_free_result($this->queryId);
        //            $this->queryId = 0;
    }

    /**
     * Db::query()
     *
     *  Sends an SQL query to the database
     *
     * @param mixed $queryString
     * @param string $line
     * @param string $file
     * @return mixed 0 if no query or query id handler, safe to ignore this return
     */
    public function query($queryString, $line = '', $file = '')
    {
        /* No empty queries, please, since PHP4 chokes on them. */
        /* The empty query string is passed on from the constructor,
        * when calling the class without a query, e.g. in situations
        * like these: '$db = new db_Subclass;'
        */
        if ($queryString == '') {
            return 0;
        }
        if (!$this->connect()) {
            return 0;
            /* we already complained in connect() about that. */
        }
        // New query, discard previous result.
        if ($this->queryId !== false) {
            $this->free();
        }

        if ($this->Debug) {
            printf("Debug: query = %s<br>\n", $queryString);
        }
        if (isset($GLOBALS['log_queries']) && $GLOBALS['log_queries'] !== false) {
            $this->log($queryString, $line, $file);
        }

        $this->queryId = $this->linkId->prepare($queryString);
        $success = $this->queryId->execute();
        $this->Rows = $this->queryId->fetchAll();
        //$this->log("PDO Query $queryString (S:$success) - ".count($this->Rows).' Rows', __LINE__, __FILE__);
        $this->Row = -1;
        if ($success === false) {
            $this->emailError($queryString, json_encode($this->queryId->errorInfo(), JSON_PRETTY_PRINT), $line, $file);
        }

        // Will return nada if it fails. That's fine.
        return $this->queryId;
    }

    /* public: walk result set */

    /**
     * Db::next_record()
     * @param mixed $resultType
     * @return bool
     */
    public function next_record($resultType = MYSQLI_ASSOC)
    {
        // PDO result types so far seem to be +1
        ++$resultType;
        if (!$this->queryId) {
            $this->halt('next_record called with no query pending.');
            return 0;
        }

        ++$this->Row;
        $this->Record = $this->Rows[$this->Row];

        $stat = is_array($this->Record);
        if (!$stat && $this->autoFree) {
            $this->free();
        }
        return $stat;
    }

    /* public: position in result set */

    /**
     * Db::seek()
     * @param integer $pos
     * @return int
     */
    public function seek($pos = 0)
    {
        if (isset($this->Rows[$pos])) {
            $this->Row = $pos;
        } else {
            $this->halt("seek($pos) failed: result has ".count($this->Rows).' rows');
            /* half assed attempt to save the day,
            * but do not consider this documented or even
            * desirable behaviour.
            */
            return 0;
        }
        return 1;
    }

    /**
     * Initiates a transaction
     * @return bool
     */
    public function transactionBegin()
    {
        return $this->linkId->beginTransaction();
    }

    /**
     * Commits a transaction
     * @return bool
     */
    public function transactionCommit()
    {
        return $this->linkId->commit();
    }

    /**
     * Rolls back a transaction
     * @return bool
     */
    public function transactionAbort()
    {
        return $this->linkId->rollBack();
    }

    /**
     * Db::getLastInsertId()
     * @param mixed $table
     * @param mixed $field
     * @return int
     */
    public function getLastInsertId($table, $field)
    {
        if (!isset($table) || $table == '' || !isset($field) || $field == '') {
            return -1;
        }
        return $this->linkId->lastInsertId();
    }

    /* public: table locking */

    /**
     * Db::lock()
     * @param mixed  $table
     * @param string $mode
     * @return void
     */
    public function lock($table, $mode = 'write')
    {
        /*            $this->connect();

        * $query = "lock tables ";
        * if (is_array($table))
        * {
        * foreach ($table as $key => $value)
        * {
        * if ($key == "read" && $key!=0)
        * {
        * $query .= "$value read, ";
        * }
        * else
        * {
        * $query .= "$value $mode, ";
        * }
        * }
        * $query = mb_substr($query,0,-2);
        * }
        * else
        * {
        * $query .= "$table $mode";
        * }
        * $res = @mysql_query($query, $this->linkId);
        * if (!$res)
        * {
        * $this->halt("lock($table, $mode) failed.");
        * return 0;
        * }
        * return $res;
        */
    }

    /**
     * Db::unlock()
     * @return void
     */
    public function unlock()
    {
        /*            $this->connect();

        * $res = @mysql_query("unlock tables");
        * if (!$res)
        * {
        * $this->halt("unlock() failed.");
        * return 0;
        * }
        * return $res;
        */
    }

    /* public: evaluate the result (size, width) */

    /**
     * Db::affectedRows()
     *
     * @return mixed
     */
    public function affectedRows()
    {
        return @$this->queryId->rowCount();
    }

    /**
     * Db::num_rows()
     * @return int
     */
    public function num_rows()
    {
        return count($this->Rows);
    }

    /**
     * Db::num_fields()
     * @return int
     */
    public function num_fields()
    {
        $keys = array_keys($this->Rows);
        return count($this->Rows[$keys[0]]);
    }

    /**
     * @param mixed $msg
     * @param string $line
     * @param string $file
     * @return mixed|void
     */
    public function haltmsg($msg, $line = '', $file = '')
    {
        $this->log("Database error: $msg", $line, $file, 'error');
        if ($this->Errno != '0' || $this->Error != '()') {
            $this->log('PDO MySQL Error: '.json_encode($this->linkId->errorInfo()), $line, $file, 'error');
        }
        $this->logBackTrace($msg, $line, $file);
    }

    /**
     * Db::tableNames()
     *
     * @return array
     */
    public function tableNames()
    {
        $return = [];
        $this->query('SHOW TABLES');
        foreach ($this->Rows as $i => $info) {
            $return[$i]['table_name'] = $info[0];
            $return[$i]['tablespace_name'] = $this->database;
            $return[$i]['database'] = $this->database;
        }
        return $return;
    }
}