PX : code

PHP database library by Peter Kankowski
Download this code


<?php

// (c) Peter Kankowski, 2007-2009  http://www.strchr.com/php_database_library  mailto:kankowski@narod.ru
// Requires PHP 5.3 or higher

// MySQL parameters
define('DB_NAME''test');
define('DB_USER''root'); // database user name
define('DB_PASSWORD'''); // database password
define('DB_HOST''localhost');

// SQLite parameters
define('DB_FILE''../../protected/test.db');


// ============================================
// Database interface

interface DBLib_DB
{
    function 
exec($query);
    
/*
        Execute a query without returning any result (INSERT, DELETE, UPDATE).
        Use additional arguments for inserting strings into the query:
            // Delete article by ID
            db()->exec('DELETE FROM "articles" WHERE "id" = ~0~', $_GET['id']);
            
            // Insert a message into log table
            db()->exec('INSERT INTO "log"("message", "date", "ip") VALUES (~0~, NOW(), ~1~)',
                $message, $_SERVER['REMOTE_ADDR']);
        
        Don't concatenate strings from an unverified source:
            db()->exec('DELETE FROM "articles" WHERE "id" = ' . $_GET['id']); // SQL injection!!!
            
        The same convention (~0~ for the first inserted string, ~1~ for the second, etc.)
        is used in get_value, get_row, and get_list functions below.
    */
    
    
function get_value($query);
    
/*
        Return a single value from DB. Angle brackets are escaped
        with htmlspecialchars. The SQL query should return one value:
            $title = db()->get_value('SELECT "title" FROM "articles" WHERE "id" = ~0~', $_GET['id']);
            echo $title;
    */
    
    
function get_row($query);
    
/*
        Return a row from DB. The htmlspecialchars function is applied to each field.
        The SQL query should return one row:
            $article = db()->get_row('SELECT * FROM "articles" WHERE "id" = ~0~', $_GET['id']);
            echo $article['title'] . '<br>' . $article['excerpt'];
    */

    
function get_value_text($query);
    function 
get_row_text($query);
    
/*
        Similar to get_value and get_row, but HTML special characters are not escaped.
        These functions can be useful if you want a plain text version of
        get_value and get_row (e.g., for generating email messages).
    */
    
    
function get_list($query);
    
/*
        Compose a list. The SQL query is executed, and the row_function is called
        for each row. There are two arguments to the function; both contain the row
        as an associative array, but in the first argument each field is escaped
        for HTML (using htmlspecialchars), while in the second arguments the fields
        are not escaped.
        
        The strings returned by row_function are concatenated and the final result
        is returned from get_list.
        
            // A list of articles
            echo '<table>' . db()->get_list(
                function($a) {return "<tr><td>{$a['title']}</td><td>{$a['author']}</td>".
                                     '<td>' . format_date($a['date']) . '</td></tr>';},
                'SELECT * FROM "articles"') . '</table>';
        
        The second argument of row_function can be useful if you want a plain text
        version of a table (e.g., for autogenerated e-mail):

            // A list of purchased items
            $list = db()->get_list(
                function($html_item, $item) {
                    return sprintf("|%-30s %10d|\r\n", $item['name'], $item['price']);
                },
                'SELECT * FROM "items", "items_in_order" '.
                'WHERE "items"."id" = "items_in_order"."item" AND '.
                      '"items_in_order"."order" = ~0~',
                $order_id);
            mail($email, 'Your order', $list);
        
        You can also use get_list to enumerate all matching rows without returning
        any values from row_function:
        
            // Send out newsletter to subscribers
            $msg = 'Hello!';
            db()->get_list(
                function($html_subscription, $subscription) use ($msg) {
                    mail($subscription['email'], 'My newsletter', $msg);
                },
                'SELECT * FROM "subscriptions"');
        
    */
    
    
function add_update($table, array $values);
    
/*
        Insert or update the table with values. If $values['id'] is set,
        the record will be updated; if not, it will be added to the table.
        The fields not present in the table are ignored. If no record
        with this ID exists, nothing will be changed in the database.
        
        The function is most useful for adding/updating data entered by user
        in a form:
            // Adding a new article or editing existing one
            db()->add_update('articles', $_POST);
        
        The function correctly escapes quotation marks. High-level data
        verification (checking presence of required fields, verifying e-mail
        address with a regular expression, etc.) should be done by the caller.
    */
}

class 
MySQL_HTML_List implements Iterator
{
    private 
$result;
    private 
$row;
    private 
$index;
    function 
__construct($result) {
        
$this->result $result;
        
$this->index 0;
        
$this->next();
    }
    function 
__destruct() {
        
mysql_free_result($this->result);
    }
    function 
current() {
        return 
$this->row;
    }
    function 
key () {
        return 
$this->index;
    }
    function 
next() {
        
$row mysql_fetch_array($this->resultMYSQL_ASSOC);
        if (!
$row) {
            
$this->row false;
            return;
        }
        
$this->row array_map('htmlspecialchars'$row);
        
$this->index++;
    }
    function 
rewind () {
        
$this->index 0;
        
mysql_data_seek($this->result$this->index);
    }
    function 
valid () {
        return 
$this->row !== false;
    }
}

class 
MySQL_DB implements DBLib_DB
{
    private 
$link;
    function 
__construct() {
        
$this->link mysql_connect(DB_HOSTDB_USERDB_PASSWORD) or $this->report_fatal_error('MySQL connect failed');
        
mysql_select_db(DB_NAME) or $this->report_fatal_error('MySQL select failed');
        
        
// Set the character set that was used for creating the database
        
$create_db $this->get_row_text('SHOW CREATE DATABASE `' DB_NAME '`') or
            
$this->report_fatal_error('Cannot get create database statement');
        
preg_match('/CHARACTER SET ([a-z0-9_]+)/i'$create_db['Create Database'], $match) or
            
$this->report_fatal_error('Cannot detect charset');
        
mysql_set_charset($match[1], $this->link) or
            
$this->report_fatal_error('Set charset failed');
        
        
// Set ANSI SQL quotes mode
        
mysql_query("SET SESSION sql_mode='ANSI_QUOTES'") or
            
$this->report_fatal_error('Set ANSI SQL quotes mode failed');
    }
    function 
__destruct() {
        
mysql_close($this->link);
    }
    private function 
report_fatal_error($err_msg) {
        die(
$err_msg '<br/>' mysql_error($this->link));
    }
    private function 
escape_args($query$args$shift) {
        
$args array_slice($args$shift);
        return 
preg_replace_callback'|~(\d+)~|',
            function (
$match) use ($args) {
                
$arg $args$match[1] ];
                return 
"'" mysql_real_escape_string($arg$this->link) . "'";
            }, 
$query );
    }
    function 
exec($query) {
        
$query $this->escape_args($queryfunc_get_args(), 1);
        
mysql_query($query$this->link) or
            
$this->report_fatal_error('Query failed ' $query);
    }
    function 
get_value_text($query) {
        
$query $this->escape_args($queryfunc_get_args(), 1);
        
$result mysql_query($query$this->link) or
            
$this->report_fatal_error('Get_value_text failed ' $query);
        
$row mysql_fetch_row($result);
        if(!
$row)
            return 
NULL;
        
mysql_free_result($result);
        return 
$row[0];
    }
    function 
get_value($query) {
        
$query $this->escape_args($queryfunc_get_args(), 1);
        
$result mysql_query($query$this->link) or
            
$this->report_fatal_error('Get_value failed ' $query);
        
$row mysql_fetch_row($result);
        if(!
$row)
            return 
NULL;
        
mysql_free_result($result);
        return 
htmlspecialchars($row[0]);
    }
    function 
get_row_text($query) {
        
$query $this->escape_args($queryfunc_get_args(), 1);
        
$result mysql_query($query$this->link) or
            
$this->report_fatal_error('Get_row_text failed ' $query);
        
$row mysql_fetch_array($resultMYSQL_ASSOC);
        if(!
$row)
            return 
NULL;
        
mysql_free_result($result);
        return 
$row;
    }
    function 
get_row($query) {
        
$query $this->escape_args($queryfunc_get_args(), 1);
        
$result mysql_query($query$this->link) or
            
$this->report_fatal_error('Get_row failed ' $query);
        
$row mysql_fetch_array($resultMYSQL_ASSOC);
        if(!
$row)
            return 
NULL;
        
mysql_free_result($result);
        
$row array_map('htmlspecialchars'$row);
        return 
$row;
    }
    function 
get_list($query) {
        
$query $this->escape_args($queryfunc_get_args(), 1);
        
$result mysql_query($query$this->link) or
            
$this->report_fatal_error('Get_list failed ' $query);
        return new 
MySQL_HTML_List($result);
    }
    function 
add_update$table, array $values ) {
        
// Get column names from DB
        
$result mysql_query'SHOW COLUMNS FROM `' .
            
mysql_real_escape_string($table$this->link) . '`'$this->link );
        
$cols '';
        
$vals '';
        
$col_val '';
        while( 
$row mysql_fetch_array($resultMYSQL_ASSOC) ) {
            
$col $row['Field'];
            if( 
$col == 'id' )
                continue;
            if( isset(
$values[$col]) ) {
                
$cols .= "`$col`,";
                
$vals  .= "'" mysql_real_escape_string($values[$col], $this->link) . "',";
                
$col_val .= "`$col`= '" mysql_real_escape_string($values[$col], $this->link) . "',";
            }
        }
        
mysql_free_result($result);
        
$cols substr($cols0, -1);
        
$vals substr($vals0, -1);
        
$col_val substr($col_val0, -1);

        if( !isset(
$values['id']) ) {
            
mysql_query("INSERT INTO `$table` ($cols) VALUES ($vals)"$this->link) or
                
$this->report_fatal_error('Insert failed');
            return 
mysql_insert_id($this->link);
        } else {
            
mysql_query"UPDATE `$table` SET ${col_val} WHERE `id` = '" .
                
mysql_real_escape_string$values['id'], $this->link ) . "'"$this->link ) or
                
$this->report_fatal_error('Update failed');
            return 
$values['id'];
        }
    }
}

class 
SQLite_DB implements DBLib_DB
{
    private 
$link;
    function 
__construct() {
        
$this->link sqlite_open(DB_FILE0600$errmsg) or
            
$this->report_fatal_error('Cannot open database<br/>' $errmsg);
    }
    function 
__destruct() {
        
sqlite_close($this->link);
    }
    private function 
report_fatal_error($err_msg) {
        die( 
$err_msg '<br/>' sqlite_error_string(sqlite_last_error($this->link)) );
    }
    private function 
escape_args($query$args$shift) {
        
$args array_slice($args$shift);
        return 
preg_replace_callback'|~(\d+)~|',
            function (
$match) use ($args) {
                
$arg $args$match[1] ];
                return 
"'" sqlite_escape_string($arg) . "'";
            }, 
$query );
    }
    function 
exec($query) {
        
$query $this->escape_args($queryfunc_get_args(), 1);
        
sqlite_exec($this->link$query) or
            
$this->report_fatal_error('Query failed ' $query);
    }
    function 
get_value_text($query) {
        
$query $this->escape_args($queryfunc_get_args(), 1);
        
$value sqlite_single_query($this->link$querytrue) or
            
$this->report_fatal_error('Get_value_text failed ' $query);
        return 
$value;
    }
    function 
get_value($query) {
        
$query $this->escape_args($queryfunc_get_args(), 1);
        
$value sqlite_single_query($this->link$querytrue) or
            
$this->report_fatal_error('Get_value failed ' $query);
        return 
htmlspecialchars($value);
    }
    function 
get_row_text($query) {
        
$query $this->escape_args($queryfunc_get_args(), 1);
        
$result sqlite_unbuffered_query($this->link$query) or
            
$this->report_fatal_error('Get_row failed ' $query);
        
$row sqlite_fetch_array($resultSQLITE_ASSOC);
        if(!
$row)
            return 
NULL;
        return 
$row;
    }
    function 
get_row($query) {
        
$query $this->escape_args($queryfunc_get_args(), 1);
        
$result sqlite_unbuffered_query($this->link$query) or
            
$this->report_fatal_error('Get_row failed ' $query);
        
$row sqlite_fetch_array($resultSQLITE_ASSOC);
        if(!
$row)
            return 
NULL;
        
$row array_map('htmlspecialchars'$row);
        return 
$row;
    }
    function 
get_list($query) {
        
$query $this->escape_args($queryfunc_get_args(), 1);
        
$result sqlite_unbuffered_query($this->link$query) or
            
$this->report_fatal_error('Get_list failed ' $query);
        return 
null;
        
/* $list = '';
        while( $row = sqlite_fetch_array($result, SQLITE_ASSOC) ) {
            $html_row = array_map('htmlspecialchars', $row);
            $list .= $row_function($html_row, $row);
        }
        return $list; */
    
}
    function 
add_update$table, array $values ) {
        
// Get column names from DB
        
$columns sqlite_fetch_column_types$table$this->link );
        
$cols '';
        
$vals '';
        
$col_val '';
        foreach( 
$columns as $col => $type ) {
            if( 
$col == 'id' )
                continue;
            if( isset(
$values[$col]) ) {
                
$cols .= "\"$col\",";
                
$vals  .= "'" sqlite_escape_string($values[$col]) . "',";
                
$col_val .= "\"$col\"= '" sqlite_escape_string($values[$col]) . "',";
            }
        }
        
$cols substr($cols0, -1);
        
$vals substr($vals0, -1);
        
$col_val substr($col_val0, -1);

        if( !isset(
$values['id']) ) {
            
sqlite_exec($this->link"INSERT INTO \"$table\" ($cols) VALUES ($vals)") or
                
$this->report_fatal_error('Insert failed');
            return 
sqlite_last_insert_rowid($this->link);
        } else {
            
sqlite_exec($this->link"UPDATE \"$table\" SET ${col_val} WHERE \"id\" = '" .
                
sqlite_escape_string$values['id'] ) . "'" ) or
                
$this->report_fatal_error('Update failed');
            return 
$values['id'];
        }
    }
}

$db = new MySQL_DB(); // Change this line to use SQLite instead of MySQL

// Define a function so that callers don't have to use "global" keyword
function db() {
    global 
$db;
    return 
$db;
}


?>

Comments or questions?
PX is running PHP 5.2.17
Thanks to Miranda Productions for hosting and bandwidth.
Use of any code from PX is at your own risk.