<?php
/*
 *      Copyright (c) 2010, JRG Productions
 *      All rights reserved.
 *
 *      Redistribution and use in source and binary forms, with or without
 *      modification, are permitted provided that the following conditions are
 *      met:
 *
 *      * Redistributions of source code must retain the above copyright
 *        notice, this list of conditions and the following disclaimer.
 *      * Redistributions in binary form must reproduce the above
 *        copyright notice, this list of conditions and the following disclaimer
 *        in the documentation and/or other materials provided with the
 *        distribution.
 *      * Neither the name of the  nor the names of its
 *        contributors may be used to endorse or promote products derived from
 *        this software without specific prior written permission.
 *
 *      THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 *      "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 *      LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 *      A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 *      OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 *      SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 *      LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 *      DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 *      THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 *      (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 *      OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */


/*
 * Standards to which my database drivers must adhere
 */
interface sqlStandard {

    // Usual query
    function query($sql);

    // For fetching rows
    function fetchRow($result null);
    function fetchAssoc($result null);
    function fetchObject($result null);

    // For fetching a single value in a row ith only one value
    function fetchOne($result null);

    // Close connection
    function close();

    // Free result
    function free($result null);

    // Free result given by last query(). Doesn't need an argument
    function freeLast();

    // Escape a string for safe query insertion
    function esc($s);

    // Die with an error message
    function errorFatal($msg);

    // Get an array of values unescaped and trimmed
    function makePresentable($arr);

}

class MySQL implements sqlStandard {

    /*
     * Gotta store connection and last result
     */
    private $connection$last_result;

    /*
     * Connect; select db
     */
    function __construct($settings) {

        /*
         * Try connecting, possibly persistently
         */
        $this->connection = (array_key_exists('persist'$settings) && $settings['persist'] == true ?
                @mysql_pconnect($settings['host'], $settings['user'], $settings['password']) :
                @mysql_connect())
            or $this->errorFatal('Error connecting: '.mysql_error());

        /*
         * Try selecting db
         */
        @mysql_select_db($settings['db'], $this->connectionor
            $this->errorFatal('Error selecting db: '.mysql_errno($this->connection));
    }

    /*
     * Undo escaping and padded white space
     */
    function makePresentable($arr) {
        if (is_array($arr))
            foreach ($arr as $k => $v)
                $arr[$k] = is_numeric($k) ? $v trim(stripslashes($v));
        return $arr;
    }

    /*
     * Run a query
     */
    public function query($sql) {

        /*
         * Free last result, if exists
         */
        $this->freelast();

        /*
         * Run query and die on error
         */
        $result @mysql_query($sql$this->connectionor
            $this->errorFatal('QUERY Error: '.mysql_error($this->connection));

        /*
         * Store result for use with $this->freeLast();
         */
        $this->last_result $result;

        /*
         * Return result
         */
        return $result;
    }

    /*
     * Return result as numeric array
     */
    public function fetchRow($result null) {
        return $this->makePresentable(@mysql_fetchRow($result == null && is_resource($this->last_result) ? $this->last_result $result));
    }

    /*
     * Return result as associative array
     */
    public function fetchAssoc($result null) {
        return $this->makePresentable(@mysql_fetchAssoc($result == null && is_resource($this->last_result) ? $this->last_result $result));
    }

    /*
     * Return result as object
     */
    public function fetchObject($result null) {
        return (object$this->fetchAssoc($result);
    }

    /*
     * Return a single value from result
     */
    public function fetchOne($result null) {
        $value $this->fetchAssoc($result);
        return is_array($value) ? (object$value $value;
    }

    /*
     * Close connection, if connected
     */
    public function close() {
        return mysql_close($this->connection);
    }

    /*
     * Free specified resource
     */
    public function free($result null) {
        if (is_resource($result))
            @mysql_free_result($result);
    }

    /*
     * Free last resource
     */
    public function freeLast() {
        if (is_resource($this->last_result))
            @mysql_free_result($this->last_result);
    }

    /*
     * Escape string for safe query insertion
     */
    public function esc($s) {
        return ctype_digit($s) ? $s mysql_real_escape_string($s$this->connection) ;
    }

    /*
     * Die with an error message
     */
    public function errorFatal($msg) {
        exit($msg);
    }
}

class PostGres implements sqlStandard {

    /*
     * Gotta store connection and last result
     */
    private $connection$last_result;

    /*
     * Connect; select db
     */
    function __construct($settings) {

        /*
         * Connection string. With port, if necessary to away from default
         */
        $cstr "host={$settings['host']} dbname={$settings['db']} user={$settings['user']} password={$settings['password']}";
        if (ctype_digit($settings['port'])  && $settings['port'] != 5432)
            $cstr .= " port={$settings['port']}";

        /*
         * Go for it
         */
        $this->connection = (array_key_exists('persist'$settings) && $settings['persist'] == true ?
            @pg_pconnect($cstr) : @pg_connect($cstr)) or
                $this->errorFatal('Cannot connect: ');
    }

    /*
     * Return result as numeric array
     */
    public function fetchRow($result null) {
        return $this->makePresentable(@pg_fetch_row(is_resource($this->last_result) ? $last_result $result));
    }

    /*
     * Return result as associative array
     */
    public function fetchAssoc($result null) {
        return $this->makePresentable(@pg_fetch_assoc(is_resource($this->last_result) ? $last_result $result));
    }

    /*
     * Return result as object
     */
    public function fetchObject($result null) {
        $value $this->fetchAssoc($result);
        return is_array($value) ? (object$value $value;
    }

    /*
     * Return a single value from object
     */
    public function fetchOne($result null) {
        $value @pg_fetch_result(is_resource($this->last_result) ? $last_result $result00);
        return $value == 't' true : ($value == 'f' false : (ctype_digit($value) ? $value stripslashes(trim($value))));
    }

    /*
     * Close connection, if connected
     */
    public function close() {
        if (is_resource($this->connection))
            pg_close($this->connection);
    }

    /*
     * Free specified resource
     */
    public function free($result null) {
        pg_free_result($result);
    }

    /*
     * Free last resource
     */
    public function freeLast() {
        if (is_resource($this->last_result))
            pg_free_result($this->last_result);
    }

    /*
     * Escape string for safe query insertion
     */
    public function esc($s) {
        return ctype_digit($s) ? $s pg_escape_string($this->connection$s);
    }

    /*
     * Escape string for safe query insertion into a bytea field
     */
    public function escBytea($s) {
        return ctype_digit($s) ? $s pg_escape_bytea($this->connection$s);
    }

    /*
     * Unescape string from bytea field
     */
    public function unescBytea($s) {
        return pg_unescape_bytea($s);
    }

    /*
     * Die with an error message
     */
    public function errorFatal($msg) {
        exit($msg);
    }


    public function query($sql) {
        $this->freelast();
        $result @pg_query($this->connection$sqlor
            $this->errorFatal('QUERY Error: '.pg_result_error_field(pg_get_result($this->connection), PGSQL_DIAG_SQLSTATE));
        $this->last_result $result;
        return $result;
    }

    function makePresentable($arr) {
        if (is_array($arr))
            foreach ($arr as $k => $v)
                $arr[$k] = is_numeric($k) ? $v trim(stripslashes($v));
        return $arr;
}


}

/*
 * Fetch connected class instance when given driver type and settings
 */
function snagDb($type$settings array()) {

    /*
     * Compatibility.
     */
    $type strtolower($type);

    /*
     * Decide what
     */
    switch ($type) {

        /*
         * MySQL
         */
        case 'mysql':
            extension_loaded('mysql'or exit('Lacking MySQLe extension');
            return new MySQL($settings);
        break;

        /*
         * PostgreSQL
         */
        case 'postgres':
            extension_loaded('pgsql'or exit('Lacking PostgreSQL extension');
            return new PostGres($settings);
        break;

        /*
         * Something either unsupported or crap
         */
        default:
            exit ('Invalid type');
        break;
    }
}