Created
March 13, 2010 21:55
-
-
Save lunks/331585 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* Base class for databases object. | |
* @author Jonathan Gotti <jgotti at jgotti dot org> | |
* @copyleft (l) 2003-2008 Jonathan Gotti | |
* @package DB | |
* @license http://opensource.org/licenses/lgpl-license.php GNU Lesser General Public License | |
* @since 2006-04-16 first splitted version | |
* get_field and list_fields have changed -> list_table_fields (list_fields indexed by name) | |
* smart '?' on conditions strings | |
* @changelog - 2008-04-10 - new class dbprofiler | |
* - 2008-04-06 - autoconnect is now a static property | |
* - now db::getInstance call require on missing class-xxxxdb.php | |
* - 2008-02-19 - now get_count() method can receive optional clause as second parameter | |
* - add method _call() and static property db::$aliases to support methods aliases to be user defined (you can set your own aliases for any methods) | |
* - rename most of select_* methods with 'better' name (shorter and better meaning I hope) But old names will work just fine with new methods aliases support | |
* - 2007-11-20 - now beverbose property is int instead of bool and can takes 4 values | |
* 0 -> no output, 1-> only errors, 2-> only queries, 3-> queries + errors | |
* (don't forget to change true by 1 in your scripts or you won't have expected output anymore) | |
* - remove call to set_error() in query_to_array only query has to do it | |
* - 2007-10-11 - change default curpage for an input in set_slice_attrs() | |
* - no more autoload of console_app when not in sapi cli | |
* - 2007-10-08 - new methods getInstance, setDefaultConnectionStr, and __destruct | |
* to ease the way of getting uniques db instances for each db | |
* - 2007-03-28 - protect_field_names() isn't called automaticly anymore to allow | |
* the use of function, wild char or alias in fields list. | |
* will perhaps permit this again with a more effective regex in the future. | |
* so at this time it's up to you to use this method as needed on any select_* method (still applyed for insert and update) | |
* - move last_q2a_res assignment from fetch_res() method to query_to_array() (seems more ligical to me) | |
* - 2007-03-26 - better fields name handling (auto-protect fieldsname even if string is given) | |
* - 2007-01-12 - better values type handling in update and insert methods | |
* - 2007-01-10 - correct a bug about page counting in set_slice_attrs() and add %page replacement to fromatStr | |
* - better params type handling in method process_conds() (int/string/array/null) | |
* - 2006-12-05 - new method select_field_to_array() | |
* - 2006-05-15 - new methods set_slice_attrs() and select_array_slice() to easily paginate your results | |
*/ | |
class dbProfiler{ | |
static public $precision = 4; | |
static public $stats = array(); | |
protected $db = null; | |
protected $statFuncs = null; | |
function __construct($dbInstance){ | |
$this->db = $dbInstance; | |
$this->statFuncs = array_merge(array_keys(db::$aliases),array_values(db::$aliases),array('delete','update','query','query_to_array')); | |
} | |
function __get($k){ | |
return $this->db->$k; | |
} | |
function __set($k,$v){ | |
return $this->db->$k = $v; | |
} | |
function __call($m,$a){ | |
if(! in_array($m,$this->statFuncs)) | |
return call_user_func_array(array($this->db,$m),$a); | |
# - get stat on queries | |
$stat = array("<b>$m</b>(".implode('<b>,</b> ',array_map(array($this,'_prepareArgStr'),$a)).')',$this->get_microtime()); | |
$res = call_user_func_array(array($this->db,$m),$a); | |
$stat[] = $this->get_microtime(); | |
self::$stats[] = $stat; | |
return $res; | |
} | |
function _prepareArgStr($a){ | |
return var_export($a,1); | |
} | |
static function printReport(){ | |
if(! count(self::$stats) ) | |
return; | |
$total = 0; | |
foreach(self::$stats as $stat){ | |
list($query,$start,$end) = $stat; | |
$time = round($end-$start,self::$precision); | |
$rows[] = '<tr><td style="border-bottom:solid silver 1px;">'.$query.'</td><td style="border-bottom:solid silver 1px;text-align:right;">'.$time.' sec</td></tr>'; | |
$total += $time; | |
} | |
echo '<table cellspacing="0" cellpadding="2" style="border:solid silver 1px;"> | |
<caption style="text-align:left;font-weight:bold;" onclick="var body = document.getElementById(\'dbProfilerReport\');body.style.display=(body.style.display==\'none\'?\'table-row-group\':\'none\')">dbProfiler report</caption> | |
<thead><tr><th style="text-align:left;border-bottom:solid silver 1px;">Query</th><th style="text-align:right;border-bottom:solid silver 1px;">time</th></tr></thead> | |
<tfoot><tr><td><b>Total: '.count(self::$stats).' queries</b></td><td><b>Total time: '.$total.'sec</b></td></tr></tfoot> | |
<tbody id="dbProfilerReport" style="display:none;">'.implode('',$rows)."</tbody> | |
</table>"; | |
} | |
function get_microtime(){ | |
list($usec, $sec) = explode(" ",microtime()); | |
return ((float)$usec + (float)$sec); | |
} | |
} | |
class db{ | |
/** array of instances already created, one for each connection strings */ | |
static protected $instances = array(); | |
/** the default connection to use */ | |
static public $defaultConnStr = null; | |
/** | |
* define methods aliases | |
* define your own like this db::$aliases[alias] = 'realMethodName'; | |
*/ | |
static public $aliases = array( | |
/** aliases required to keep backward compatibility */ | |
'select_to_array' => 'select_rows', | |
'select_single_to_array' => 'select_row', | |
'select2associative_array' => 'select_associative', | |
'select_single_value' => 'select_value', | |
'select_array_slice' => 'select_slice', | |
'select_field_to_array' => 'select_col', | |
/** camelCase style aliases (for my own pleasure) */ | |
'selectRows' => 'select_rows', | |
'selectRow' => 'select_row', | |
'selectAssociative' => 'select_associative', | |
'selectCol' => 'select_col', | |
'selectSlice' => 'select_slice', | |
'selectValue' => 'select_value', | |
); | |
/**Db hostname*/ | |
public $host = null; | |
/**mysql username*/ | |
public $user = null; | |
/**mysql password*/ | |
public $pass = null; | |
/**resource connection (same as $conn if not applicable)*/ | |
public $conn = null; | |
/**resource db selected*/ | |
public $db = null ; | |
/**selected database*/ | |
public $dbname = ''; | |
/** resource result handler*/ | |
public $last_qres = null; | |
/**array of last query to array results*/ | |
public $last_q2a_res = array(); | |
/**array of error number and msgs*/ | |
public $error = array(); | |
/**the last error array*/ | |
public $last_error = array(); | |
/** | |
* set the level of verbosity. | |
* It MUST be an integar not a string or nothing will output!!!! | |
* 0 -> no output, 1-> only errors, 2-> only queries, 3-> queries + errors | |
*/ | |
public $beverbose = 0; | |
static public $autoconnect = TRUE; | |
/** | |
*chr to protect fields names in queries | |
*@private | |
*/ | |
public $_protect_fldname = '`'; | |
/** | |
* return a single instance of the database corresponding to the given connection String. | |
* This method must be copyed in the exended class as php is not able to get the name of the calling class (one more poor aspect of this language). | |
* @param string $connectionStr the connection string is a semi colon separated list | |
* of connection parameter in the order they appear in the constructor | |
* preceeded by classname:// | |
* for exemple a mysqldb connection string will look like: | |
* "mysqldb://dbname;dbhost:port;dbuser;dbpass" | |
* and a sqlitedb one will look like: | |
* "sqlitedb://dbfile;mode" | |
* @param bool $setDefault if true then this database connection will be the default one | |
* returned when no arguments are given. | |
* For conveniance the first call to this method | |
* will set the corresponding instance the default one | |
* if none has been set before | |
* @return db instance | |
*/ | |
static public function getInstance($connectionStr=null,$setDefault=false){ | |
if( is_null(self::$defaultConnStr) ){ | |
if(is_null($connectionStr)) | |
throw new Exception(__class__." Can't return an instance without any valid connection string."); | |
self::$defaultConnStr = $connectionStr; | |
} | |
if(is_null($connectionStr)) | |
$connectionStr = self::$defaultConnStr; | |
if(isset(self::$instances[$connectionStr])) | |
return self::$instances[$connectionStr]; | |
list($class,$params) = explode('://',$connectionStr); | |
$params = explode(';',$params); | |
$paramNb = count($params); | |
for($i=0;$i<$paramNb;++$i){ | |
$pEval[] = "\$params[$i]"; | |
} | |
if(! class_exists($class) ) | |
require (dirname(__file__)."/class-$class.php"); | |
eval( '$instance = new '.$class.'('.implode(',',$pEval).');'); | |
return self::$instances[$connectionStr] = $instance; | |
} | |
static public function setDefaultConnectionStr($connectionStr){ | |
self::$defaultConnStr = $connectionStr; | |
} | |
/** | |
* This way of creating an instance is not encourage anymore! | |
* @deprecated use @see getInstance instead | |
* constructor stay public even if we have getInstance for 2 reason | |
* 1- backward compatibility with existing scripts | |
* 2- permit getInstance to create any derived class without redefining it in each subclass | |
*/ | |
public function __construct(){ | |
if(self::$autoconnect) | |
$this->open(); | |
} | |
public function __destruct(){ | |
foreach( self::$instances as $k=>$db){ | |
self::$instances[$k]->close(); | |
unset($db,self::$instances[$k]); | |
} | |
$this->close(); #- close current object connection if not obtained by getInstance | |
} | |
function __call($m,$a){ | |
if( isset(self::$aliases[$m]) ) | |
return call_user_func_array(array($this,self::$aliases[$m]),$a); | |
} | |
###*** REQUIRED METHODS FOR EXTENDED CLASS ***### | |
/** open connection to database */ | |
public function open(){} | |
/** close connection to previously opened database */ | |
public function close(){} | |
/** | |
* Select the database to work on (it's the same as the use db command or mysql_select_db function) | |
* @param string $dbname | |
* @return bool | |
* / | |
function select_db($dbname=null){}*/ | |
/** | |
* take a resource result set and return an array of type 'ASSOC','NUM','BOTH' | |
* @see sqlitedb or mysqldb implementation for exemple | |
*/ | |
public function fetch_res($result_set,$result_type='ASSOC'){} | |
public function last_insert_id(){} | |
/** | |
* base method you should replace this one in the extended class, to use the appropriate escape func regarding the database implementation | |
* @param string $quotestyle (both/single/double) which type of quote to escape | |
* @return str | |
*/ | |
public function escape_string($string,$quotestyle='both'){ | |
$escapes = array("\x00", "\x0a", "\x0d", "\x1a", "\x09","\\"); | |
$replace = array('\0', '\n', '\r', '\Z' , '\t', "\\\\"); | |
switch(strtolower($quotestyle)){ | |
case 'double': | |
case 'd': | |
case '"': | |
$escapes[] = '"'; | |
$replace[] = '\"'; | |
break; | |
case 'single': | |
case 's': | |
case "'": | |
$escapes[] = "'"; | |
$replace[] = "\'"; | |
break; | |
case 'both': | |
case 'b': | |
case '"\'': | |
case '\'"': | |
$escapes[] = '"'; | |
$replace[] = '\"'; | |
$escapes[] = "'"; | |
$replace[] = "\'"; | |
break; | |
} | |
return str_replace($escapes,$replace,$string); | |
} | |
/** | |
* perform a query on the database | |
* @param string $Q_str | |
* @return= result id | FALSE | |
**/ | |
public function query($Q_str){} | |
/** | |
* perform a query on the database like query but return the affected_rows instead of result | |
* give a most suitable answer on query such as INSERT OR DELETE | |
* @param string $Q_str | |
* @return int affected_rows or FALSE on error! | |
* @can work without this method but less smart | |
function query_affected_rows($Q_str){} | |
*/ | |
/** | |
* get the table list from $this->dbname | |
* @return array | |
*/ | |
public function list_tables(){} | |
/** | |
* return the list of field in $table | |
* @param string $table name of the sql table to work on | |
* @param bool $extended_info if true will return the result of a show field query in a query_to_array fashion | |
* (indexed by fieldname instead of int if false) | |
* @return array | |
*/ | |
public function list_table_fields($table,$extended_info=FALSE){} | |
/** Verifier si cette methode peut s'appliquer a SQLite */ | |
public function show_table_keys($table){} | |
/** | |
* optimize table statement query | |
* @param string $table name of the table to optimize | |
* @return bool | |
*/ | |
public function optimize($table){} | |
public function error_no(){} | |
public function error_str($errno=null){} | |
###*** COMMON METHODS ***### | |
/** | |
* return the result of a query to an array | |
* @param string $Q_str SQL query | |
* @param string $result_type 'ASSOC', 'NUM' et 'BOTH' | |
* @return array | false if no result | |
*/ | |
public function query_to_array($Q_str,$result_type='ASSOC'){ | |
$this->last_q2a_res = array(); | |
if(! $this->query($Q_str)){ | |
return FALSE; | |
} | |
return $this->last_q2a_res = $this->fetch_res($this->last_qres,$result_type); | |
} | |
/** | |
* send a select query to $table with arr $fields requested (all by default) and with arr $conditions | |
* @param string|array $Table | |
* @param string|array $fields | |
* @param string|array $conditions | |
* @param string $res_type 'ASSOC', 'NUM' et 'BOTH' | |
* @Return array | false | |
**/ | |
public function select_rows($tables,$fields = '*', $conds = null,$result_type = 'ASSOC'){ | |
//we make the table list for the Q_str | |
if(! $tb_str = $this->array_to_str($tables)) | |
return FALSE; | |
//we make the fields list for the Q_str | |
if(! $fld_str = $this->array_to_str($fields) ) | |
$fld_str = '*'; | |
//now the WHERE str | |
$conds_str = $this->process_conds($conds); | |
$Q_str = "SELECT $fld_str FROM $tb_str $conds_str"; | |
# echo "SQL : $Q_str\n;"; | |
return $this->query_to_array($Q_str,$result_type); | |
} | |
/** | |
* Same as select_rows but return only the first row. | |
* equal to $res = select_rows followed by $res = $res[0]; | |
* @see select_rows for details | |
* @return array of fields | |
*/ | |
public function select_row($tables,$fields = '*', $conds = null,$result_type = 'ASSOC'){ | |
if(! $res = $this->select_rows($tables,$fields,$conds,$result_type)) | |
return FALSE; | |
return $res[0]; | |
} | |
/** | |
* just a quick way to do a select_rows followed by a associative_array_from_q2a_res | |
* see both thoose method for more information about parameters or return values | |
*/ | |
public function select_associative($tables,$fields='*',$conds=null,$index_field='id',$value_fields=null,$keep_index=FALSE){ | |
if(! $this->select_rows($tables,$fields,$conds)) | |
return FALSE; | |
return $this->associative_array_from_q2a_res($index_field,$value_fields,null,$keep_index); | |
} | |
/** | |
* select a single value in database | |
* @param string $table | |
* @param string $field the field name where to pick-up value | |
* @param mixed conds | |
* @return mixed or FALSE | |
*/ | |
public function select_value($table,$field,$conds=null){ | |
if($res = $this->select_row($table,$field,$conds,'NUM')) | |
return $res[0]; | |
else | |
return FALSE; | |
} | |
/** | |
* select a single table field and return all values | |
* @param string $table | |
* @param string $field name of the single field to retrieve | |
* @param mixed $conds | |
* @return array or FALSE | |
*/ | |
public function select_col($table,$field,$conds=null){ | |
$conds_str = $this->process_conds($conds); | |
$Q_str = "SELECT $field FROM $table $conds_str"; | |
if(! $res = $this->query_to_array($Q_str,'NUM') ) | |
return FALSE; | |
foreach($res as $row){ | |
$_res[] = $row[0]; | |
} | |
return $_res; | |
} | |
/** | |
* @return array array((array) results,(str) navigationstring, (int) totalrows) | |
*/ | |
public function select_slice($table,$fields='*',$conds=null,$pageId=1,$pageNbRows=10){ | |
$conds = $this->process_conds($conds); | |
if(! ($tot = $this->select_value($table,'count(*)',$conds) ) ) | |
return FALSE; | |
$limitStart = (int) $pageNbRows * ($pageId-1); | |
$res = $this->select_rows($table,$fields,$conds." Limit $limitStart,$pageNbRows"); | |
# now prepare navigation links | |
$attrs = $this->set_slice_attrs(); | |
extract($attrs); | |
$nbpages = ceil($tot/max(1,$pageNbRows)); | |
# start/prev link | |
if($nbpages > 1 && $pageId != 1){ | |
$first = str_replace('%lnk',str_replace('%page',1,$linkStr),$first); | |
$prev = str_replace('%lnk',str_replace('%page',$pageId-1,$linkStr),$prev); | |
}else{ | |
$first = $prev = ''; | |
} | |
# next/end link | |
if( $pageId < $nbpages ){ | |
$last = str_replace('%lnk',str_replace('%page',$nbpages,$linkStr),$last); | |
$next = str_replace('%lnk',str_replace('%page',$pageId+1,$linkStr),$next); | |
}else{ | |
$last = $next = ''; | |
} | |
# pages links | |
if(preg_match('!%(\d+)?links!',$formatStr,$m)){ | |
$nblinks = isset($m[1])?$m[1]:''; | |
if(! $nblinks){ # all pages links | |
$slideStart = 1; | |
$slideEnd = $nbpages; | |
}else{ # range pages link | |
$delta = $nblinks%2?($nblinks-1)/2:$nblinks/2; | |
$slideStart = max(1,$pageId - $delta - (($pageId+$delta)<=$nbpages?0: $pageId -($nbpages-$delta)) ); | |
$slideEnd = min($nbpages,$pageId + $delta + ($pageId > $delta?0: $delta - $pageId + 1 ) ); | |
} | |
for($i=$slideStart;$i<=$slideEnd;$i++){ | |
$pageLinks[] = str_replace( array('%lnk','%page'), | |
array(str_replace('%page',$i,$linkStr),$i), | |
($i==$pageId?$curpage:$pages) | |
); | |
} | |
$links = implode($linkSep,$pageLinks); | |
} | |
$formatStr = str_replace( array('%first','%prev','%next','%last','%'.$nblinks.'links','%tot','%nbpages','%page'), | |
array($first,$prev,$next,$last,$links,$tot,$nbpages,$pageId), | |
$formatStr | |
); | |
return array($res,$formatStr,$tot); | |
} | |
/** | |
* set attributes for slice rendering. | |
* take an associative array of format strings to render slice links. | |
* - firt: first page link %lnk and %page will be replaced by the link to the page and the number of the page | |
* - prev: previous page link %lnk and %page will be replaced by the link to the page and the number of the page | |
* - next: next page link %lnk and %page will be replaced by the link to the page and the number of the page | |
* - last: last page link %lnk and %page will be replaced by the link to the page and the number of the page | |
* - pages: pages link %lnk and %page will be replaced by the link to the page and the number of the page | |
* - curpage: selected page link %lnk and %page will be replaced by the link to the page and the number of the page | |
* - linkStr: is used for rendering the url of pages %page will be replaced by the corresponding page number | |
* - linkSep: separator between pages links | |
* - formatStr: is used to render the full pagination string | |
* %start, %prev, %next, %last will be replaced respectively by corresponding links | |
* %Nlinks will be replaced by the pages links. N is the number of link to display | |
* including the selected page ex: %5links will show 5 pages links | |
* you can pass only the keys you want to replace ex: db::set_slice_attrs(array('linkStr'=>"myslice.php?page=%page")) | |
* all keys can also contain a %tot and %nbpages which will be replaced respectively by | |
* the total amount of result and the total number of pages | |
*@param array $attrs | |
*@return array | |
*/ | |
public function set_slice_attrs($attrs=null){ | |
static $sliceAttrs; | |
if(! isset($sliceAttrs) ){ | |
$sliceAttrs = array( 'first' => "<a href=\"%lnk\" class=\"pagelnk\"><<</a>", | |
'prev' => "<a href=\"%lnk\" class=\"pagelnk\"><</a>", | |
'next' => "<a href=\"%lnk\" class=\"pagelnk\">></a>", | |
'last' => "<a href=\"%lnk\" class=\"pagelnk\">>></a>", | |
'pages' => "<a href=\"%lnk\" class=\"pagelnk\">%page</a>", | |
#- 'curpage' => "<b><a href=\"%lnk\" class=\"pagelnk\">%page</a></b>", | |
'curpage' => '<input type="text" value="%page" onfocus="this.value=\'\';" onkeydown="if(event.keyCode==13){ var p=parseInt(this.value)||1;window.location=\'%lnk\'.replace(/page=%page/,\'page=\'+(p>%nbpages?%nbpages:(p<1?1:p)));return false;}" size="3" title="aller à la page" style="text-align:center;" />', | |
'linkStr' => "?page=%page", | |
'linkSep' => " ", | |
'formatStr'=> " %first %prev %5links %next %last" | |
); | |
} | |
if( is_array($attrs) ){ | |
foreach($sliceAttrs as $k=>$v){ | |
$sliceAttrs[$k] = isset($attrs[$k])?$attrs[$k]:$v; | |
} | |
} | |
return $sliceAttrs; | |
} | |
/** | |
* Send an insert query to $table | |
* @param string $table | |
* @param array $values (arr(FLD=>VALUE,) | |
* @param bool $return_id the function will return the inserted_id if $return_id is true (the default value), else it'll return only true or false. | |
* @return insert id or FALSE | |
**/ | |
public function insert($table,$values,$return_id=TRUE){ | |
if(!is_array($values)) | |
return FALSE; | |
$fld = $this->protect_field_names(array_keys($values)); | |
$val = array_map(array($this,'prepare_smart_param'),$values); | |
$Q_str = "INSERT INTO $table ($fld) VALUES (".$this->array_to_str($val).")"; | |
if(! $this->query($Q_str) ) | |
return FALSE; | |
$this->last_id = $this->last_insert_id(); | |
return $return_id?$this->last_id:TRUE; | |
} | |
/** | |
* Send a delete query to $table | |
* @param string $table | |
* @param mixed $conds | |
* @return int affected_rows | |
**/ | |
public function delete($table,$conds=null){ | |
$conds_str = $this->process_conds($conds); | |
$Q_str = "DELETE FROM $table $conds_str"; | |
if(method_exists($this,'query_affected_rows')){ | |
$res = $this->query_affected_rows($Q_str); | |
return ($res===FALSE || $res === -1)?FALSE:$res; | |
}else{ | |
$count = (int) $this->get_count($table); | |
if(! $this->query($Q_str) ) | |
return FALSE; | |
$count2 = (int) $this->get_count($table); | |
return (int) ($count - $count2); | |
} | |
} | |
/** | |
* Send an update query to $table | |
* @param string $table | |
* @param string|array $values ( 'fld=value, fld2=value2' arr(FLD=>VALUE,)) | |
* @return int affected_rows or bool (depends on the database implementation (have we a query_affected_rows or not?)) | |
**/ | |
public function update($table,$values,$conds = null){ | |
if(is_array($values)){ | |
$str = array(); | |
foreach( $values as $k=>$v) | |
$str[] = $this->protect_field_names($k)." = ".$this->prepare_smart_param($v).' '; | |
}elseif(! is_string($values)){ | |
return FALSE; | |
} | |
# now the WHERE str | |
$conds_str = $this->process_conds($conds); | |
$Q_str = "UPDATE $table SET ".(is_array($values)?$this->array_to_str($str):$values)." $conds_str"; | |
if(method_exists($this,'query_affected_rows')){ | |
$res = $this->query_affected_rows($Q_str); | |
return ($res===FALSE || $res === -1)?FALSE:$res; | |
}else{ | |
return (bool) $this->query($Q_str); | |
} | |
} | |
/** | |
* get the number of row in $table | |
* @param string $table table name | |
* @param mixed $conds | |
* @return int | |
*/ | |
public function get_count($table,$conds=null){ | |
return $this->select_value($table,'count(*) as c',$conds); | |
} | |
/** | |
*return an associative array indexed by $index_field with values $value_fields from | |
*a mysqldb->select_rows result | |
*@param string $index_field default value is id | |
*@param mixed $value_fields (string field name or array of fields name default is null so keep all fields | |
*@param array $res the mysqldb->select_rows result | |
*@param bool $keep_index if set to true then the index field will be keep in the values associated (unused if $value_fields is string) | |
*@param bool $sort_keys will automaticly sort the array by key if set to true @deprecated argument | |
*@return array | |
*/ | |
public function associative_array_from_q2a_res($index_field='id',$value_fields=null,$res = null,$keep_index=FALSE,$sort_keys=FALSE){ | |
if($res===null) | |
$res = $this->last_q2a_res; | |
if(! is_array($res)){ | |
$this->verbose("associative_array_from_q2a_res with invalid result",__FUNCTION__,1); | |
return FALSE; | |
} | |
# then verify index exists | |
if(!isset($res[0][$index_field])){ | |
$this->verbose("associative_array_from_q2a_res with invalid index field '$index_field'",__FUNCTION__,1); | |
return FALSE; | |
} | |
# then we do the trick | |
if(is_string($value_fields)){ | |
foreach($res as $row) | |
$associatives_res[$row[$index_field]] = $row[$value_fields]; | |
}elseif(is_array($value_fields)||$value_fields===null){ | |
foreach($res as $row){ | |
$associatives_res[$row[$index_field]] = $row; | |
if(!$keep_index) | |
unset($associatives_res[$row[$index_field]][$index_field]); | |
} | |
} | |
if(! count($associatives_res)) | |
return FALSE; | |
if($sort_keys) | |
ksort($associatives_res); | |
return $this->last_q2a_res = $associatives_res; | |
} | |
/*########## INTERNAL METHOD ##########*/ | |
/** | |
* used by other methods to parse the conditions param of a QUERY. | |
* If $conds is string then nothing more is done. | |
* If it's an array, the first value (index 0) will be consider as the full condition string and all '?' will be replaced by other values in the array (sort of sprintf). | |
* You can add a number before a ? to replace it by a given index in the array like 2? | |
* @param string|array $conds | |
* @return string | |
*/ | |
public function process_conds($conds=null){ | |
if(is_string($conds) ) | |
return $conds; | |
elseif(! is_array($conds) ) | |
return ''; | |
$conds_str = array_shift($conds); | |
array_unshift($conds,''); | |
$i=0; | |
return preg_replace('!(\d*)\?!e',"\$this->prepare_smart_param('\\1'!==''?\$conds['\\1']:(isset(\$conds[++\$i])?\$conds[\$i]:null),'single')",$conds_str); | |
} | |
/** | |
* used internally for smart params processing | |
* @private | |
*/ | |
protected function prepare_smart_param($val){ | |
if(is_null($val)){ | |
return 'NULL'; | |
}elseif (is_int($val) || is_float($val)) { | |
return $val; | |
} elseif(is_string($val)) { | |
return "'".$this->escape_string($val,'single')."'"; | |
} elseif(is_array($val)) { | |
return implode(',', array_map(array(&$this,'prepare_smart_param'),$val)); | |
}else{ | |
return "''"; | |
} | |
} | |
/** | |
* used internally to prepare fields for queries | |
* @param string|array $fields list of fields. it's up to you to protect fieldsname if you put in fields as string | |
*/ | |
public function protect_field_names($fields){ | |
if(is_array($fields)){ | |
foreach($fields as $k=>$f) | |
$fields[$k] = $this->_protect_fldname.$f.$this->_protect_fldname; | |
$fields = implode(',',$fields); | |
}elseif($fields){ | |
if( $this->_protect_fldname && ! substr_count($fields,$this->_protect_fldname) ){ # if already protected we do nothing | |
$fields = preg_replace('!\s*,\s*!',$this->_protect_fldname.','.$this->_protect_fldname,$fields); | |
$fields = $this->_protect_fldname . trim($fields) . $this->_protect_fldname; | |
} | |
} | |
return $fields?$fields:false; | |
} | |
protected function array_to_str($var,$sep=','){ | |
return (is_string($var)?$var:(is_array($var)?implode($sep,$var):'')); | |
} | |
protected function set_error($callingfunc=null){ | |
static $i=0; | |
if(! $this->db ){ | |
$this->error[$i]['nb'] = null; | |
$this->error[$i]['str'] = '[ERROR] No Db Handler'; | |
}else{ | |
$this->error[$i]['nb'] = $this->error_no(); | |
$this->error[$i]['str'] = $this->error_str($this->error[$i]['nb']); | |
} | |
$this->last_error = $this->error[$i]; | |
$this->verbose($this->error[$i]['str'],$callingfunc,1); | |
$i++; | |
} | |
/** | |
* print a msg on STDOUT if $this->beverbose is set to true | |
* @param string $msg message output string | |
* @param string $callingFunc name of the calling function | |
* @param int $msgLvl the level corresponding to the message | |
* 1-> error message | |
* 2-> query or similar informative message | |
* msgLvl must be an int not a string as it will be test by type | |
* @private | |
*/ | |
protected function verbose($msg,$callingFunc=null,$msgLvl=1){ | |
if(! $this->beverbose) | |
return; | |
if( ($msgLvl===2 && $this->beverbose >=2) || ($msgLvl === 1 && $this->beverbose !== 2) ){ | |
$msg = get_class($this).($callingFunc?"::$callingFunc":'').' => '.$msg; | |
$useConsoleApp = ( php_sapi_name()=='cli' && class_exists('console_app',false))?true:false; | |
$isError = $msgLvl===1?true:false; | |
if($isError){ | |
if($useConsoleApp) | |
return console_app::msg_error($msg); | |
echo "<b style=\"color:red;\">[ERROR] $msg</b><br />\n"; | |
}else{ | |
if($useConsoleApp) | |
return console_app::msg_info($msg); | |
echo "<b style=\"color:blue;\">$msg</b><br />\n"; | |
} | |
} | |
} | |
###*** DEPRECATED METHODS ***### | |
/** | |
* return the list of field in $table | |
* @deprecated still here for compatibility with old version | |
* @use and @see db::list_table_fields() instead | |
* @param string $table name of the sql table to work on | |
* @param bool $extended_info will return the result of a show field query in a query_to_array fashion | |
*/ | |
public function get_fields($table,$extended_info=FALSE){ | |
return $this->list_table_fields($table,$extended_info); | |
} | |
/** | |
* get the fields list of table | |
* @deprecated now the $indexed_by_name args won't exists anymore but will considered as TRUE in all case | |
* @see db::list_table_fields as a replacement method | |
* @param string $table | |
* @param bool $indexed_by_name the return array will be indexed by the fields name if set to true (default is FALSE) | |
* @return array | |
*/ | |
public function list_fields($table,$indexed_by_name=FALSE){ | |
return $this->list_table_fields($table,TRUE); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment