<?php
final class tht
{
/**
* @var array
* A SQL safe COPY of _REQUEST
**/
private $request = array( ) ;
/**
* @var resource
* MySQL connection
**/
private $mysql = null ;
/**
* @var array
* Internal errors
**/
private $errors = array( ) ;
/**
* @var integer
* Number of queries so far
**/
private $count = 0 ;
/**
* @param array configuration loaded from disk
* Although methods/variables are static, __constructing allows
* for population of the objects static member variables with useful data
**/
function __construct( &$config )
{
// Do whatever you normally would with request to make it sql safe
if( $_REQUEST )
{
if( get_magic_quotes_gpc( ) )
{
self::$request = self::apply( $_REQUEST, 'stripslashes' );
}
else self::$request = $_REQUEST ;
// now tht::request( 'key' ); will return _REQUEST['key'] SQL safe
}
// Connect to MySQL with config loaded from disk
if( ( self::$mysql = mysql_connect( $config['hostname'], $config['username'], $config['password'] ) ) )
{
// Make sure we have the required database available
if( mysql_select_db( $config['database'], self::$mysql ) )
{
// start filling other members with useful data
// config was passed by reference, so you could be useful and fill it with configuration data
// from the database, then you can keep passing this config array by reeference to other objects
// used in execution
}
else self::error( __METHOD__, "Failed to select %s on %s, MySQL said: %s", __FILE__, __LINE__, $config['database'], $config['hostname'], mysql_error( self::$mysql ) );
}
else self::error( __METHOD__, "Failed to connect to %s as %s with password supplied, MySQL said: %s", __FILE__, __LINE__, $config['hostname'], $config['username'], mysql_error( ) );
}
/**
* @param array the array to apply the callback too
* @param string the name of the callback to apply to array
* Recursively applies a callback to every member in an array and returns a COPY
**/
static function apply( $array, $callback = null )
{
$clean = array( );
foreach( $array as $key => $value )
{
if( is_array( $value ) )
{
$clean[$key] = self::apply( $value, $callback );
}
else $clean[$key] = call_user_func( $callback, $value );
}
return $clean ;
}
/**
* @param string the name of the member to fetch
* If key is omitted return the whole request array ( for templates perhaps )
**/
static function request( $key = null )
{
switch( is_null( $key ) )
{
case true: return self::$request; break;
case false: return self::$request[$key]; break;
}
return $key ;
}
/**
* @param string method the error occured in
* @param string message describing the error ( sprintf compatible )
* @param string file the error occured in
* @param line line the error occured in
* @param format variable number of parameters to format message with ( see __construct )
* Set an internal error, and format it too ...
**/
static function error( $method, $message, $file, $line, $format = null )
{
if( ( $format = func_get_args( ) ) )
{
if( ( $method = array_shift( $format ) ) )
{
if( ( $message = array_shift( $format ) ) )
{
if( ( $file = array_shift( $format ) ) )
{
if( ( $line = array_shift( $format ) ) )
{
return ( $this->errors[] = vsprintf
(
"{$method}: {$message} in {$file} on line {$line}",
$format
) );
}
}
}
}
}
}
/**
* @param boolean delete errors where true and errors exist
* Return all the formatted errors, optionally delete them before returning
**/
static function errors( $delete = false )
{
$errors = array( );
if( count( self::$errors ) )
{
$errors = self::$errors ;
switch( $delete )
{
case true: self::$errors = array( ); break;
}
}
return $errors ;
}
/**
* @param string the SQL to send
* @param mixed variable length list of things to escape and format sql with
* tht::query( "SELECT * FROM settings" );
* tht::query( "SELECT * FROM table WHERE column = '%d'", tht::request('key') );
* returns MySQL resource, sets errors and escapes anything after sql
**/
static function & query( $sql, $format = null )
{
if( self::$mysql )
{
if( ( $format = func_get_args( ) ) )
{
if( ( $sql = array_shift( $format ) ) )
{
$result = null ;
if( $format )
{
foreach( $format as $key => $value )
{
if( $value )
{
$format[$key] = mysql_real_escape_string( $value, self::$mysql );
}
}
if( !( $sql = vsprintf( $sql, $format ) ) )
{
return self::error( __METHOD__, "Failed to format SQL with %d parameters", __FILE__, __LINE__, count( $format ) );
}
}
if( ( $result = mysql_query( $sql, self::$mysql ) ) )
{
self::$count++;
}
else self::error( __METHOD__, "Failed to execute query, MySQL said: %s", __FILE__, __LINE__, mysql_error( self::$mysql ) );
return $result ;
}
else self::error( __METHOD__, "Method called improperly", __FILE__, __LINE__ );
}
else self::error( __METHOD__, "Method called improperly", __FILE__, __LINE__ );
}
else self::error( __METHOD__, "An attempt was made to execute SQL on an invalid connection", __FILE__, __LINE__ );
}
/**
* @param resource MySQL result from tht::query
* wrap of mysql_fetch_assoc for uniform code
* don't try to error handle tht::next because while $x = tht::next($result)
* will cause errors when there are none except no more results
**/
static function next( &$result )
{
if( $result )
{
return mysql_fetch_assoc( $result );
}
}
/**
* @param resource MySQL result from tht::query
* wrap of mysql_fetch_assoc for uniform code
* you could beef it up some with error handling on non-existent results etc
**/
static function field( &$result, $index = 0, $column = null )
{
if( $result )
{
return mysql_fetch_result( $result, $index, $column );
}
}
/**
* @param tht|resource|null what to count ??
* tht::count( 'tht' ) = number of queries executed so far
* tht::count( $result ) = number of rows contained by $result
* tht::count( ) = number of rows affected by the execution of $result
**/
static function count( $what = null )
{
switch( $what )
{
case __CLASS__: return self::$count; break;
case null; return mysql_affected_rows( self::$mysql ); break;
default: return mysql_num_rows( $what ); break;
}
}
/**
* wrap of mysql_close for uniform code
**/
static function close( )
{
if( self::$mysql )
{
@mysql_close( self::$mysql );
}
self::$mysql = null ;
}
}
?>