<?php
// DbPDO.php
// Copyright P.Kassel, M.Seliger, G.Trietsch, all Rights Reserved
namespace Docuform\Database;global $g_Database_Debug;if(!isset($g_Database_Debug)){$g_Database_Debug=0;}use DateTimeZone;use Docuform\Lib\ApiDateTime;use Exception;use Throwable;use PDO;use PDOException;use PDOStatement;define('SQLSRV_ATTR_ENCODING',1000);define('SQLSRV_ATTR_QUERY_TIMEOUT',1001);define('SQLSRV_ATTR_DIRECT_QUERY',1002);define('SQLSRV_ATTR_CURSOR_SCROLL_TYPE',1003);define('SQLSRV_ATTR_CLIENT_BUFFER_MAX_KB_SIZE',1004);define('SQLSRV_ATTR_FETCHES_NUMERIC_TYPE',1005);class DbException extends Exception{public function __construct(string $message='',int $code=0,Throwable|null $previous=null){parent::__construct($message,$code,$previous);}public function __toString():string{return __CLASS__.": ['Database error']: {$this->message}\n";}}class DbPDO extends PDO{private $m_DriverName;private $m_NativeDataType;private $m_SelectForUpdate;private $m_debug;private $m_decimalSeparator='.';private $m_nonUtf8Charset=false;public function __construct(mixed $dsn,mixed $username=null,mixed $password=null,mixed $driver_options=null){global $g_Database_Debug;parent::__construct($dsn,$username,$password,$driver_options);$this->m_debug=$g_Database_Debug;$this->setAttribute(PDO::ATTR_ERRMODE,PDO::ERRMODE_WARNING);$this->setAttribute(PDO::ATTR_ERRMODE,PDO::ERRMODE_EXCEPTION);if($this->m_debug===1){$this->setAttribute(PDO::ATTR_STATEMENT_CLASS,[DbPDOStatement::class,[$this]]);}if(false!==stripos($dsn,'access')){$this->m_DriverName='ACCESS';}elseif(false!==stripos($dsn,'sql native')){$this->m_DriverName='SQLSERVER';}elseif(false!==stripos($dsn,'sql server')){$this->m_DriverName='SQLSERVER';}elseif(false!==stripos($dsn,'sqlsrv:')){$this->m_DriverName='SQLSERVER';}elseif(false!==stripos($dsn,'tds')){$this->m_DriverName='SQLSERVER';}elseif(false!==stripos($dsn,'mysql')){$this->m_DriverName='MYSQL';}elseif(false!==stripos($dsn,'sqlite')){$this->m_DriverName='SQLITE';}elseif(false!==stripos($dsn,'firebird')){$this->m_DriverName='FIREBIRD';}if($this->m_DriverName==='ACCESS'){$this->m_SelectForUpdate=false;$this->m_NativeDataType['SQL_TIMESTAMP']='DATETIME';$this->m_NativeDataType['SQL_INTEGER']='LONG';$this->m_NativeDataType['SQL_VARCHAR']='TEXT(255)';$this->m_NativeDataType['SQL_WCHAR']='MEMO';}elseif($this->m_DriverName==='MYSQL'){$this->setAttribute(PDO::MYSQL_ATTR_INIT_COMMAND,'SET NAMES utf8mb4');$this->setAttribute(PDO::ATTR_EMULATE_PREPARES,false);$this->m_SelectForUpdate=true;$this->m_NativeDataType['SQL_TIMESTAMP']='DATETIME';$this->m_NativeDataType['SQL_INTEGER']='INT';$this->m_NativeDataType['SQL_VARCHAR']='VARCHAR(255)';$this->m_NativeDataType['SQL_WCHAR']='MEDIUMTEXT';}elseif($this->m_DriverName==='SQLSERVER'){$this->m_SelectForUpdate=true;$this->m_NativeDataType['SQL_TIMESTAMP']='datetime2(0)';$this->m_NativeDataType['SQL_INTEGER']='int';$this->m_NativeDataType['SQL_VARCHAR']='varchar(255)';$this->m_NativeDataType['SQL_WCHAR']='nvarchar(255)';}elseif($this->m_DriverName==='SQLITE'){$this->m_SelectForUpdate=false;$this->m_NativeDataType['SQL_TIMESTAMP']='TIMESTAMP';$this->m_NativeDataType['SQL_INTEGER']='INTEGER';$this->m_NativeDataType['SQL_VARCHAR']='VARCHAR(255)';$this->m_NativeDataType['SQL_WCHAR']='WVARCHAR(255)';}else{$this->m_SelectForUpdate=false;$this->m_NativeDataType['SQL_TIMESTAMP']='datetime2';$this->m_NativeDataType['SQL_INTEGER']='int';$this->m_NativeDataType['SQL_VARCHAR']='varchar(255)';$this->m_NativeDataType['SQL_WCHAR']='wvarchar(255)';}}public function getNonUtf8Charset():bool{return $this->m_nonUtf8Charset;}public function setNonUtf8Charset(bool $setting):void{$this->m_nonUtf8Charset=$setting;}private function decodeUtf8(&$value):void{if($this->m_nonUtf8Charset){if(is_string($value)){$value=utf8_decode($value);}elseif(is_array($value)){foreach($value as &$array_value){$this->decodeUtf8($array_value);}unset($array_value);}elseif(is_object($value)){$vars=array_keys(get_object_vars($value));foreach($vars as $var){$this->decodeUtf8($value->$var);}}}}private function encodeUtf8(&$value):void{if($this->m_nonUtf8Charset){if(is_string($value)){$IsUtf8=preg_match('%(?:
                              [\xC2-\xDF][\x80-\xBF]              # non-overlong 2-byte
                              |\xE0[\xA0-\xBF][\x80-\xBF]         # excluding overlongs
                              |[\xE1-\xEC\xEE\xEF][\x80-\xBF]{2}  # straight 3-byte
                              |\xED[\x80-\x9F][\x80-\xBF]         # excluding surrogates
                              |\xF0[\x90-\xBF][\x80-\xBF]{2}      # planes 1-3
                              |[\xF1-\xF3][\x80-\xBF]{3}          # planes 4-15
                              |\xF4[\x80-\x8F][\x80-\xBF]{2}      # plane 16
                              )+%xs',$value);$value=$IsUtf8?$value:utf8_encode($value);}elseif(is_array($value)){foreach($value as &$array_value){$this->encodeUtf8($array_value);}unset($array_value);}elseif(is_object($value)){$vars=array_keys(get_object_vars($value));foreach($vars as $var){$this->encodeUtf8($value->$var);}}}}public function setlocalizeDecimalSeparator(string $separator):void{$this->m_decimalSeparator=$separator;}public function localizeFloatValue($value){if($value!==null&&$this->m_decimalSeparator!=='.'){if(strpos((string)$value,$this->m_decimalSeparator)!==false){$value=str_replace($this->m_decimalSeparator,'',(string)$value);}return str_replace('.',$this->m_decimalSeparator,(string)$value);}return $value;}public function IsMySQL():bool{return $this->m_DriverName==='MYSQL';}public function IsSQLServer():bool{return $this->m_DriverName==='SQLSERVER';}public function EscapeValue():string{return($this->m_DriverName==='MYSQL')?' escape \'~\'':'';}public function BIGINT($Field):string{if($this->m_DriverName==='ACCESS'){$Result='{fn CONVERT('.$Field.',SQL_NUMERIC)}';}elseif($this->m_DriverName==='MYSQL'){$Result='CAST('.$Field.' AS signed)';}else{$Result='CAST('.$Field.' AS BIGINT)';}return $Result;}public function CONCAT($Strings):string{if($this->m_DriverName==='ACCESS'||$this->m_DriverName==='SQLSERVER'){$Result='{fn CONCAT('.implode(',',$Strings).')}';}else{$Result='CONCAT('.implode(',',$Strings).')';}return $Result;}public function FetchAll(string $SQL,mixed $Default=null,?array $Params=null,int $FetchStyle=PDO::FETCH_ASSOC):false|array{$Result=false;if(isset($SQL)&&!empty($SQL)){$Cursor=$this->Execute($SQL,$Params);if($Cursor!==false){$Result=$Cursor->fetchAll($FetchStyle);$Cursor->closeCursor();$this->encodeUtf8($Result);}}if($Result===false&&isset($Default)){$Result=$Default;}return $Result;}public function Execute(string $sql,?array $Params=null,bool $IgnoreError=false):false|PDOStatement{$Cursor=false;$Exception=false;if(isset($sql)&&!empty($sql)){try{$Cursor=$this->prepare($sql);$this->decodeUtf8($Params);if(($Cursor!==false)&&$Cursor->execute($Params)===false){$Exception='Error executing database statement';}}catch(PDOException $e){$Exception=$e->getMessage().' ';if($this->IsAccess()){$Exception=iconv("UTF-8","Windows-1252",$Exception);}if($Cursor!==false){$Cursor->closeCursor();$Cursor=false;}}}if(($Cursor===false||$Exception!==false)&&$IgnoreError===false&&$this->m_debug===1){$trace=debug_backtrace();$info=$this->errorInfo();$this->setExceptionInfo($Exception,$trace,$info,$sql);}return $Cursor;}public function setExceptionInfo(string $msg,array $backtrace,array $err,?string $SQL=null):void{if($this->m_debug===1){$LastError=$msg;if($err!==null&&is_array($err)){$LastError.="<hr>Error info: [$err[0]] ".($err[1]?? '').' : '.($err[2]?? '');}$trace='';while(($backtrace!==null)&&(($caller=array_pop($backtrace))!==null)&&isset($caller['file'])){$trace.='-> '.$caller['file'].':'.$caller['line'].'<br>';}if($trace!==''){$LastError.='<hr>'.$trace;}if(!empty($SQL)){$LastError.='<hr>'.$SQL.'<hr>';}throw new DbException($LastError);}throw new DbException('Database error');}public function FetchNext($Cursor,$FetchStyle=PDO::FETCH_ASSOC){$Result=false;if($Cursor&&$Cursor!==false){$Result=$Cursor->fetch($FetchStyle);$this->encodeUtf8($Result);}return $Result;}public function FetchIntoObject(object $Object,string $SQL,?array $Params=null):mixed{$Result=false;if(isset($SQL)&&!empty($SQL)&&!empty($Object)){$Cursor=$this->Execute($SQL,$Params);if($Cursor!==false){$Cursor->setFetchMode(PDO::FETCH_INTO,$Object);$Result=$Cursor->fetch();$Cursor->closeCursor();$this->encodeUtf8($Result);}}return $Result;}public function FetchAllObjects(int $limit,string $sql,array $params,string $className,array $ctorArgs=array()){$result=false;if(!empty($sql)&&!empty($className)){if($limit){$sql=$this->LIMIT($sql,$limit);}$cursor=$this->Execute($sql,$params);if($cursor!==false){$result=$cursor->fetchAll(PDO::FETCH_CLASS|PDO::FETCH_PROPS_LATE,$className,$ctorArgs);$cursor->closeCursor();if($limit&&is_array($result)&&count($result)>$limit){$result=array_slice($result,0,$limit);}$this->encodeUtf8($result);}}return $result;}public function FetchObject(string $sql,array $params,string $className,array $ctorArgs=array()){$result=false;if(!empty($sql)&&!empty($className)){$cursor=$this->Execute($sql,$params);if($cursor!==false){$cursor->setFetchMode(PDO::FETCH_CLASS|PDO::FETCH_PROPS_LATE,$className,$ctorArgs);$result=$cursor->fetch();$cursor->closeCursor();$this->encodeUtf8($result);}}return $result;}public function FetchObjects($limit,string $sql,array $params,string $className,array $ctorArgs=array()){if(is_bool($limit)&&$limit===false){return $this->FetchObject($sql,$params,$className,$ctorArgs);}return $this->FetchAllObjects(is_bool($limit)?0:$limit,$sql,$params,$className,$ctorArgs);}public function GetDatabase():string{return $this->m_DriverName;}public function GetLocking():bool{return $this->m_SelectForUpdate;}public function GetNativeType($SqlType):string{$Result='';if(!empty($SqlType)&&isset($this->m_NativeDataType[$SqlType])){$Result=$this->m_NativeDataType[$SqlType];}return $Result;}public function GetRowCount(string $SQL,?array $Params=null):int{$Result=0;if(isset($SQL)&&!empty($SQL)){$Result=$this->FetchColumn('SELECT COUNT(*) as Num FROM ('.$SQL.') as RowSel',0,$Params);}return $Result;}public function FetchColumn(string $SQL='',mixed $Default=null,?array $Params=null):mixed{$Result=false;if(isset($SQL)&&!empty($SQL)){$Cursor=$this->Execute($SQL,$Params);if($Cursor!==false){$Result=$Cursor->fetchColumn();$Cursor->closeCursor();$this->encodeUtf8($Result);}}if($Result===false&&isset($Default)){$Result=$Default;}return $Result;}public function EscapeString($sText):string{return str_replace("'","''",$sText);}public function CONVERT($sColumn,$type):string{$Result='';if(($this->m_debug===1)&&!is_string($type)){throw new DbException('Invalid parameter type in dmfPDO::CONVERT: '.$type);}if($this->m_DriverName==='MYSQL'){$desttype=['SQL_VARCHAR'=>'char','SQL_INTEGER'=>'signed','SQL_FLOAT'=>'signed'];if(isset($desttype[$type])){$Result='CAST('.$sColumn.' as '.$desttype[$type].')';}}else{$desttype=['SQL_VARCHAR'=>'SQL_VARCHAR','SQL_INTEGER'=>'SQL_INTEGER','SQL_FLOAT'=>'SQL_FLOAT'];if(isset($desttype[$type])){$Result='{fn CONVERT('.$sColumn.', '.$desttype[$type].')}';}}return $Result;}public function ISNULL($sVal):string{return $this->IIF($sVal.' is null','1','0');}public function IIF($sIF,$sTHEN,$sELSE):string{if($this->m_DriverName==='ACCESS'){$Result='IIF('.$sIF.','.$sTHEN.','.$sELSE.')';}elseif($this->m_DriverName==='MYSQL'){$Result='IF('.$sIF.','.$sTHEN.','.$sELSE.')';}else{$Result='CASE WHEN '.$sIF.' THEN '.$sTHEN.' ELSE '.$sELSE.' END';}return $Result;}public function LIKE($sLike,$escape='~'){$sLike=str_replace("'","''",$sLike);if($this->m_DriverName==='MYSQL'){$sLike=str_replace(['%','_'],[$escape.'%',$escape.'_'],$sLike);}else{$sLike=str_replace(['[','%','_'],['[[]','[%]','[_]'],$sLike);}return $sLike;}public function NOW():string{return '{fn NOW()}';}public function ROUND($floatval,$digits):string{return 'ROUND('.$floatval.', '.$digits.')';}public function CHECKBIT($Column,$BitValue,$bIsTrue):string{return $this->MOD($Column,2*$BitValue).($bIsTrue?'>=':'<').(int)$BitValue;}public function MOD($Dividend,$Divider):string{if($this->m_DriverName==='ACCESS'){$Result='{fn MOD('.$Dividend.', '.$Divider.')}';}elseif($this->m_DriverName==='SQLSERVER'){$Result='(('.$Dividend.') % ('.$Divider.'))';}else{$Result='(MOD('.$Dividend.','.$Divider.'))';}return $Result;}public function SELECTBIT($Column,$BitValue):string{return $this->IIF('('.$this->MOD($Column,2*$BitValue).')>='.$BitValue,1,0);}public function SWITCH($switch,$else):string{if($this->IsAccess()){$Result='SWITCH (';foreach($switch as $key=>$value){$Result.=$key.', '.$value.', ';}$Result.='1, 0)';}else{$Result='CASE';foreach($switch as $key=>$value){$Result.=' WHEN '.$key.' THEN '.$value;}$Result.=' ELSE '.$else.' END';}return $Result;}public function IsAccess():bool{return $this->m_DriverName==='ACCESS';}public function TableExists($table):bool{try{$sql=$this->LIMIT("SELECT 1 as 'Test' FROM ".$table,1);$result=(!empty($table)&&$this->query($sql));}catch(Exception $e){return false;}return $result!==false;}public function TIMESTAMPDIFF_DAYS($val1,$val2):string{if($this->m_DriverName==='ACCESS'){$diff="DATEDIFF('d', ".$val1.', '.$val2.')';}else{$diff='{fn TIMESTAMPDIFF(SQL_TSI_DAY, '.$val1.', '.$val2.')}';}return $diff;}public function TIMESTAMPDIFF_MIN($val1,$val2):string{if($this->m_DriverName==='ACCESS'){$diff="DATEDIFF('n', ".$val1.', '.$val2.')';}else{$diff='{fn TIMESTAMPDIFF(SQL_TSI_MINUTE, '.$val1.', '.$val2.')}';}return $diff;}public function getDbValue($Table,$Fields,$Condition='',$Params=[],$Default=''){try{$Cursor=null;if($Fields==='*'){$Fields=$this->getColumns($Table);}if($Fields!==false&&is_array($Fields)){$Columns=implode(',',$Fields);}else{$Columns=$Fields;}$Query='SELECT '.$Columns.' FROM '.$Table;if($Condition!==''){$Query.=' WHERE '.$Condition;}if(is_array($Fields)){$Result=$this->FetchFirst($Cursor,$Query,$Params);if($Result===false){$Result=[];foreach($Fields as $aField){$Result[$aField]='';}}else{$this->encodeUtf8($Result);}if(isset($Default)&&$Default!==''){foreach($Result as $key=>$value){if($value===''){$Result[$key]=$Default;}}}}else{$Result=$this->FetchFirst($Cursor,$Query,$Params,PDO::FETCH_NUM);if($Result!==false){$Result=$Result[0];$this->encodeUtf8($Result);}else{$Result='';}if($Result===''&&isset($Default)){$Result=$Default;}}}catch(Exception $e){$Result=false;}$this->FetchClose($Cursor);return $Result;}public function getColumns($Table){$Cursor=null;$Fields=false;try{$SQL='SELECT * FROM '.$Table;$this->LIMIT($SQL,1);$Row=$this->FetchFirst($Cursor,$SQL);if($Row!==false){$Fields=[];foreach($Row as $key=>$value){$Fields[]=$key;}}}catch(Exception $e){$Fields=false;}if($Cursor!==null){$this->FetchClose($Cursor);}return $Fields;}public function LIMIT($SQL,$MaxRows):string{$Result=$SQL;if($this->m_DriverName==='ACCESS'||$this->m_DriverName==='SQLSERVER'){$p=stripos($SQL,'SELECT DISTINCT ');if($p!==false){$Result=substr($SQL,0,$p).'SELECT DISTINCT TOP '.$MaxRows.' '.substr($SQL,$p+16);}else{$p=stripos($SQL,'SELECT ');if($p!==false){$Result=substr($SQL,0,$p).'SELECT TOP '.$MaxRows.' '.substr($SQL,$p+7);}}}elseif($this->m_DriverName==='MYSQL'){$Result='('.$SQL.') LIMIT '.$MaxRows;}elseif($this->m_DriverName==='FIREBIRD'){$p=stripos($SQL,'SELECT ');if($p!==false){$Result=substr($SQL,0,$p).'SELECT FIRST '.$MaxRows.' '.substr($SQL,$p+7);}}return $Result;}public function ROWS($sql,$order,$start,$count):string{if($this->IsSQLServer()){$sql='WITH Records AS (SELECT row_number() OVER(ORDER BY '.$order.") as 'pkRow', *".' FROM ('.$sql.') as pkInt)'.' SELECT * FROM Records'.' WHERE pkRow>='.$start.' AND pkRow<'.($start+$count).' ORDER BY '.$order;}elseif($this->IsMySQL()){$sql.=' ORDER BY '.$order.' LIMIT '.($start-1).','.$count;}else{return false;}return $sql;}public function FetchFirst(false|PDOStatement|null & $Cursor=null,string $SQL='',?array $Params=null,int $FetchStyle=PDO::FETCH_ASSOC):false|array{$Result=false;if(!empty($SQL)){if($Cursor&&$Cursor!==false){$Cursor->closeCursor();}$Cursor=$this->Execute($SQL,$Params);if($Cursor!==false){$Result=$Cursor->fetch($FetchStyle);$this->encodeUtf8($Result);}}return $Result;}public function FetchClose(& $Cursor):void{if($Cursor&&$Cursor!==false){$Cursor->closeCursor();$Cursor=false;}}public function createDateTimeFromDb($db_time):ApiDateTime{$dt=null;if(isset($db_time)&&$db_time!==null){$dt=new ApiDateTime(null,null);$this->fromDbDate($dt,$db_time);}return $dt;}public function fromDbDate($dt,$db_timestamp):bool{$ret=false;if($db_timestamp!==null&&$db_timestamp!==''){$year=substr($db_timestamp,0,4);$mon=substr($db_timestamp,5,2);$day=substr($db_timestamp,8,2);$trenn=$db_timestamp[10];$hour=substr($db_timestamp,11,2);$dp1=$db_timestamp[13];$min=substr($db_timestamp,14,2);$dp2=$db_timestamp[16];$sec=substr($db_timestamp,17,2);if($trenn===' '&&$dp1===':'&&$dp2===':'&&checkdate($mon,$day,$year)){$dt->setTimezone(new DateTimeZone('UTC'));$dt->setDate($year,$mon,$day);$dt->setTime($hour,$min,$sec);$ret=true;}}return $ret;}public function getTimestampFromDbDate($db_time):int{$unix_timestamp=0;if(isset($db_time)&&$db_time!==null){$dt=new ApiDateTime(null,null);$this->fromDbDate($dt,$db_time);$unix_timestamp=$dt->getTimestamp();}return $unix_timestamp;}public function getDateFromDbDate($db_time,$format=null):string{$date='';if(isset($db_time)&&$db_time!==null){$dt=new ApiDateTime(null,null);$this->fromDbDate($dt,$db_time);$date=$dt->format($format);}return $date;}public function getLocalDateFromDbDate($db_time,$format):string{$local='';if(isset($db_time)&&$db_time!==null){$dt=new ApiDateTime(null,null);$this->fromDbDate($dt,$db_time);$local=$dt->getLocalDate($format);}return $local;}public function quotedDbDate($dtobject):string{$dtquoted='null';if(isset($dtobject)&&$dtobject!=null){$dtquoted="{ts '".$dtobject->format('Y-m-d H:i:s')."'}";}return $dtquoted;}public function FetchedDbDate($dtObject):string{if(isset($dtObject)&&$dtObject!=null){$dbdate=$dtObject->format('Y-m-d H:i:s');}else{$dbdate='null';}return $dbdate;}public function NowDb():string{$dtObject=new ApiDateTime();return $this->DbDate($dtObject);}public function DbDate($dtobject):string{if(isset($dtobject)&&$dtobject!=null){if($this->m_DriverName==='ACCESS'){$dbdate=$dtobject->format('Y-m-d H:i:s');}else{$dbdate=$dtobject->toIso(false);}}else{$dbdate='null';}return $dbdate;}}class DbPDOStatement extends PDOStatement{public $m_query='';private $m_pdo;private $m_pdo_can_quote=true;private $m_params=[];private $m_debug;protected function __construct(?DbPDO $pdo=null){global $g_Database_Debug;$this->m_pdo=$pdo;$this->m_debug=$g_Database_Debug;}public function bindParam($parameter,&$variable,$data_type=PDO::PARAM_STR,$length=0,$driver_options=null):bool{$this->m_params[$parameter]=['value'=>&$variable,'datatype'=>$data_type];return parent::bindParam($parameter,$variable,$data_type,$length,$driver_options);}public function bindValue($parameter,$value,$data_type=PDO::PARAM_STR):bool{$this->m_params[$parameter]=['value'=>$value,'datatype'=>$data_type];return parent::bindValue($parameter,$value,$data_type);}public function execute($input_parameters=null):bool{$ret=false;$this->m_query=$this->GetQueryString($input_parameters);try{$ret=parent::execute($input_parameters);if($ret===false&&$this->m_debug===1){$bt=debug_backtrace();$err=$this->m_pdo->errorInfo();$this->m_pdo->setExceptionInfo('execute error',$bt,$err,$this->m_query);}}catch(PDOException $e){$Exception=$e->getMessage();if($this->m_pdo->IsAccess()){$Exception=iconv("Windows-1252","UTF-8",$Exception);}if($this->m_debug===1){$bt=debug_backtrace();$err=$this->m_pdo->errorInfo();$this->m_pdo->setExceptionInfo($Exception,$bt,$err,$this->m_query);}}return $ret;}public function GetQueryString($params=null):string{$Result=$this->queryString;$queryParams=empty($this->m_params)?$params:$this->m_params;if($queryParams){ksort($queryParams);foreach($queryParams as $key=>$value){$param=is_array($value)?$value:['value'=>$value,'datatype'=>PDO::PARAM_STR];$param=$this->prepare($param);$Result=$this->replaceParam($Result,$key,$param);}}return $Result;}private function prepare($param):string{if($param['value']===null){return 'NULL';}if($param['datatype']===PDO::PARAM_INT){return (int)$param['value'];}if(is_numeric($param['value'])){return $param['value'];}if($this->m_pdo){try{$Result=$this->m_pdo_can_quote?$this->m_pdo->quote($param['value']):false;}catch(Exception $e){$Result=false;$this->m_pdo_can_quote=false;}}else{$Result=false;}if($Result===false){$Result="'".$param['value']."'";}return $Result;}private function replaceParam($queryString,$param,$value){$param=is_numeric($param)?"\?":(preg_match('/^:/',$param)?$param:':'.$param);$pattern="/({$param}(?!\w))(?=(?:[^\"']|[\"'][^\"']*[\"'])*$)/";return preg_replace($pattern,$value,$queryString,1);}}