commit 0d18c4c3666fc39a449baa05b13d162a6753a3c3 Author: David Grudl Date: Sun Jun 4 23:06:33 2006 +0000 update to 0.5alpha diff --git a/copyright.txt b/copyright.txt new file mode 100644 index 00000000..ad2e2f22 --- /dev/null +++ b/copyright.txt @@ -0,0 +1,21 @@ +Copyright notice +---------------- + +dibi (C) David Grudl, 2005-2006 + +For more information, visit the homepage http://texy.info/dibi +or author's weblog: http://www.dgx.cz/trine/ + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA diff --git a/dibi.compact/dibi-compact.php b/dibi.compact/dibi-compact.php new file mode 100644 index 00000000..489bbb5e --- /dev/null +++ b/dibi.compact/dibi-compact.php @@ -0,0 +1,33 @@ + + * @link http://texy.info/dibi/ + * @copyright Copyright (c) 2005-2006 David Grudl + * @license GNU GENERAL PUBLIC LICENSE + * @package dibi + * @category Database + * @version 0.5alpha (2006-05-26) for PHP5 + */define('dibi','Database Abstraction Layer (c) David Grudl, http://texy.info/dibi/');if(version_compare(PHP_VERSION,'5.0.3','<'))die('dibi needs PHP 5.0.3 or newer'); + abstract class DibiDriver{protected$config;public$formats=array('NULL'=>"NULL",'TRUE'=>"1",'FALSE'=>"0",'date'=>"'Y-m-d'",'datetime'=>"'Y-m-d H:i:s'",);abstract static public function connect($config);protected function __construct($config){$this->config=$config;}public function getConfig(){return$this->config;}abstract public function query($sql);abstract public function affectedRows();abstract public function insertId();abstract public function begin();abstract public function commit();abstract public function rollback();abstract public function escape($value,$appendQuotes=FALSE);abstract public function quoteName($value);abstract public function getMetaData();} + if(!interface_exists('Countable',false)){interface Countable{function count();}}abstract class DibiResult implements IteratorAggregate,Countable{const FIELD_TEXT='s',FIELD_BINARY='b',FIELD_BOOL='l',FIELD_INTEGER='i',FIELD_FLOAT='f',FIELD_DATE='d',FIELD_DATETIME='t',FIELD_UNKNOWN='?',FIELD_COUNTER='c';protected$convert;abstract public function seek($row);abstract public function rowCount();abstract public function getFields();abstract public function getMetaData($field);abstract protected function detectTypes();abstract protected function free();abstract protected function doFetch();final public function fetch(){$rec=$this->doFetch();if(!is_array($rec))return FALSE;if($t=$this->convert){foreach($rec as$key=>$value){if(isset($t[$key]))$rec[$key]=$this->convert($value,$t[$key]);}}return$rec;}final function fetchSingle(){$rec=$this->doFetch();if(!is_array($rec))return FALSE;if($t=$this->convert){$value=reset($rec);$key=key($rec);return isset($t[$key])?$this->convert($value,$t[$key]):$value;}return reset($rec);}final function fetchAll(){@$this->seek(0);$rec=$this->fetch();if(!$rec)return array();$assocBy=func_get_args();$arr=array();if(!$assocBy){$value=count($rec)==1?key($rec):NULL;do{$arr[]=$value===NULL?$rec:$rec[$value];}while($rec=$this->fetch());return$arr;}do{foreach($assocBy as$n=>$assoc){$val[$n]=$rec[$assoc];unset($rec[$assoc]);}foreach($assocBy as$n=>$assoc){if($n==0)$tmp=&$arr[$val[$n]];else$tmp=&$tmp[$assoc][$val[$n]];if($tmp===NULL)$tmp=$rec;}}while($rec=$this->fetch());return$arr;}final function fetchPairs($key,$value){@$this->seek(0);$rec=$this->fetch();if(!$rec)return array();$arr=array();do{$arr[$rec[$key]]=$rec[$value];}while($rec=$this->fetch());return$arr;}public function __destruct(){@$this->free();}public function setType($field,$type=NULL){if($field===TRUE)$this->detectTypes();elseif(is_array($field))$this->convert=$field;else$this->convert[$field]=$type;}public function getType($field){return isset($this->convert[$field])?$this->convert[$field]:NULL;}public function convert($value,$type){if($value===NULL||$value===FALSE)return$value;static$conv=array(self::FIELD_TEXT=>'string',self::FIELD_BINARY=>'string',self::FIELD_BOOL=>'bool',self::FIELD_INTEGER=>'int',self::FIELD_FLOAT=>'float',self::FIELD_COUNTER=>'int',);if(isset($conv[$type])){settype($value,$conv[$type]);return$value;}if($type==self::FIELD_DATE)return new TDate($value);if($type==self::FIELD_DATETIME)return new TDateTime($value);return$value;}public function getIterator($offset=NULL,$count=NULL){return new DibiResultIterator($this,$offset,$count);}public function count(){return$this->rowCount();}}class DibiResultIterator implements Iterator{private$result,$offset,$count,$record,$row;public function __construct(DibiResult$result,$offset=NULL,$count=NULL){$this->result=$result;$this->offset=(int)$offset;$this->count=$count===NULL?PHP_INT_MAX:(int)$count;}public function rewind(){$this->row=0;@$this->result->seek($this->offset);$this->record=$this->result->fetch();}public function key(){return$this->row;}public function current(){return$this->record;}public function next(){$this->record=$this->result->fetch();$this->row++;}public function valid(){return is_array($this->record)&&($this->row<$this->count);}} + class DibiParser{private$modifier,$hasError,$driver;public function parse($driver,$args){$sql='';$this->driver=$driver;$this->modifier=0;$this->hasError=false;$command=null;$lastString=null;foreach($args as$index=>$arg){$sql.=' ';if(is_array($arg)){if($this->modifier){$type=$this->modifier;$this->modifier=false;}else{if(is_int(key($arg)))$type='L';else{if(!$command)$command=strtoupper(substr(ltrim($args[0]),0,6));$type=$command=='UPDATE'?'S':'V';}}$vx=$kx=array();switch($type){case'S':foreach($arg as$k=>$v)$vx[]=$this->driver->quoteName($k).'='.$this->formatValue($v);$sql.=implode(', ',$vx);break;case'V':foreach($arg as$k=>$v){$kx[]=$this->driver->quoteName($k);$vx[]=$this->formatValue($v);}$sql.='('.implode(', ',$kx).') VALUES ('.implode(', ',$vx).')';break;case'L':foreach($arg as$k=>$v)$vx[]=$this->formatValue($v);$sql.=implode(', ',$vx);break;case'N':foreach($arg as$v)$vx[]=$this->driver->quoteName($v);$sql.=implode(', ',$vx);break;default:$this->hasError=true;$sql.="**Unknown modifier %$type**";}continue;}if($this->modifier){if($arg instanceof IDibiVariable){$sql.=$arg->toSql($this->driver,$this->modifier);$this->modifier=false;continue;}if(!is_scalar($arg)&&!is_null($arg)){$this->hasError=true;$this->modifier=false;$sql.='**Unexpected '.gettype($arg).'**';continue;}switch($this->modifier){case"s":$sql.=$this->driver->escape($arg,TRUE);break;case'T':$sql.=date($this->driver->formats['date'],is_string($arg)?strtotime($arg):$arg);break;case't':$sql.=date($this->driver->formats['datetime'],is_string($arg)?strtotime($arg):$arg);break;case'b':$sql.=$arg?$this->driver->formats['TRUE']:$this->driver->formats['FALSE'];break;case'i':case'u':case'd':$sql.=(string)(int)$arg;break;case'f':$sql.=(string)(float)$arg;break;case'n':$sql.=$this->driver->quoteName($arg);break;default:$this->hasError=true;$sql.="**Unknown modifier %$this->modifier**";}$this->modifier=false;continue;}if(is_string($arg)){$lastString=$index;$toSkip=strcspn($arg,'`[\'"%');if($toSkip==strlen($arg)){$sql.=$arg;}else{$sql.=substr($arg,0,$toSkip).preg_replace_callback('/ + (?=`|\[|\'|"|%) ## speed-up + (?: + `(.+?)`| ## 1) `identifier` + \[(.+?)\]| ## 2) [identifier] + (\')((?:\'\'|[^\'])*)\'| ## 3,4) string + (")((?:""|[^"])*)"| ## 5,6) "string" + %([a-zA-Z])$| ## 7) right modifier + (\'|") ## 8) lone-quote + )/xs',array($this,'callback'),substr($arg,$toSkip));}continue;}$sql.=$this->formatValue($arg);}if($this->hasError)return new DibiException('Errors during generating SQL',array('sql'=>$sql));return trim($sql);}private function formatValue($value){if(is_string($value))return$this->driver->escape($value,TRUE);if(is_int($value)||is_float($value))return(string)$value;if(is_bool($value))return$value?$this->driver->formats['TRUE']:$this->driver->formats['FALSE'];if(is_null($value))return$this->driver->formats['NULL'];if($value instanceof IDibiVariable)return$value->toSql($this->driver);$this->hasError=true;return'**Unsupported type '.gettype($value).'**';}private function callback($matches){if($matches[1])return$this->driver->quoteName($matches[1]);if($matches[2])return$this->driver->quoteName($matches[2]);if($matches[3])return$this->driver->escape(strtr($matches[4],array("''"=>"'")),true);if($matches[5])return$this->driver->escape(strtr($matches[6],array('""'=>'"')),true);if($matches[7]){$this->modifier=$matches[7];return'';}if($matches[8]){return'**Alone quote**';$this->hasError=true;}die('this should be never executed');}} + class DibiException extends Exception{private$info;public function __construct($message,$info=NULL){$this->info=$info;if(isset($info['message']))$message="$message: $info[message]";parent::__construct($message);}public function getSql(){return@$this->info['sql'];}}function is_error($var){return($var===FALSE)||($var instanceof Exception);} + if(function_exists('date_default_timezone_set'))date_default_timezone_set('Europe/Prague');class TDate implements IDibiVariable{protected$time;public function __construct($time=NULL){if($time===NULL)$this->time=time();elseif(is_string($time))$this->time=strtotime($time);else$this->time=(int)$time;}public function toSQL($driver,$modifier=NULL){return date($driver->formats['date'],$this->time);}public function getTimeStamp(){return$this->time;}}class TDateTime extends TDate{public function toSQL($driver,$modifier=NULL){return date($driver->formats['datetime'],$this->time);}} interface IDibiVariable{public function toSQL($driver,$modifier=NULL);}class dibi{static private$registry=array();static private$conn;static private$parser;static public$sql;static public$error;static public$logfile;static public$debug=false;static private$query=array();static public function connect($config,$name='def'){if(!self::$parser)self::$parser=new DibiParser();if(isset(self::$registry[$name]))return new DibiException("Connection named '$name' already exists.");if(empty($config['driver']))return new DibiException('Driver is not specified.');$className="Dibi$config[driver]Driver"; if(!class_exists($className))return new DibiException("Unable to create instance of dibi driver class '$className'.");$conn=call_user_func(array($className,'connect'),$config);if(self::$logfile!=NULL){if(is_error($conn))$msg="Can't connect to DB '$config[driver]': ".$conn->getMessage();else$msg="Successfully connected to DB '$config[driver]'";$f=fopen(self::$logfile,'a');fwrite($f,"$msg\r\n\r\n");fclose($f);}if(is_error($conn)){if(self::$debug)echo'[dibi error] '.$conn->getMessage();return$conn;}self::$conn=self::$registry[$name]=$conn;return TRUE;}static public function isConnected(){return(bool)self::$conn;}static public function getConnection($name=NULL){return$name===NULL?self::$conn:@self::$registry[$name];}static public function activate($name){if(!isset(self::$registry[$name]))return FALSE;self::$conn=self::$registry[$name];return TRUE;}static public function query(){if(!self::$conn)return new DibiException('Dibi is not connected to DB');$args=func_num_args()?func_get_args():self::$query;self::$query=array();self::$sql=self::$parser->parse(self::$conn,$args);if(is_error(self::$sql))return self::$sql;$timer=-microtime(true);$res=self::$conn->query(self::$sql);$timer+=microtime(true);if(is_error($res)){if(self::$debug){echo'[dibi error] '.$res->getMessage();self::dump(self::$sql);}self::$error=$res;}else{self::$error=FALSE;}if(self::$logfile!=NULL){if(is_error($res))$msg=$res->getMessage();elseif($res instanceof DibiResult)$msg='object('.get_class($res).') rows: '.$res->rowCount();else$msg='OK';$f=fopen(self::$logfile,'a');fwrite($f,self::$sql.";\r\n-- Result: $msg"."\r\n-- Takes: ".sprintf('%0.3f',$timer*1000).' ms'."\r\n\r\n");fclose($f);}return$res;}static public function queryStart(){self::$query=func_get_args();}static public function queryAdd(){$args=func_get_args();self::$query=array_merge(self::$query,$args);}static public function test(){if(!self::$conn)return FALSE;$args=func_num_args()?func_get_args():self::$query;self::$query=array();$sql=self::$parser->parse(self::$conn,$args);if(is_error($sql)){self::dump($sql->getSql());return$sql->getSql();}else{self::dump($sql);return$sql;}}static public function insertId(){if(!self::$conn)return FALSE;return self::$conn->insertId();}static public function affectedRows(){if(!self::$conn)return FALSE;return self::$conn->affectedRows();}static public function dump($sql){static$highlight=array('ALL','DISTINCT','AS','ON','INTO','AND','OR','AS',);static$newline=array('SELECT','UPDATE','INSERT','DELETE','FROM','WHERE','HAVING','GROUP BY','ORDER BY','LIMIT','SET','VALUES','LEFT JOIN','INNER JOIN',);foreach($newline as$word)$sql=preg_replace('#\b'.$word.'\b#',"\n\$0",$sql);$sql=trim($sql);$sql=wordwrap($sql,100);$sql=htmlSpecialChars($sql);$sql=strtr($sql,array("\n"=>'
'));foreach($newline as$word)$sql=preg_replace('#\b'.$word.'\b#','$0',$sql);foreach($highlight as$word)$sql=preg_replace('#\b'.$word.'\b#','$0',$sql);$sql=preg_replace('#\*\*.+?\*\*#','$0',$sql);echo'
',$sql,'
';}static public function dumpResult(DibiResult$res){echo'';echo'';$fieldCount=$res->fieldCount();for($i=0;$i<$fieldCount;$i++){$info=$res->fieldMeta($i);echo'';}echo'';foreach($res as$row=>$fields){echo'';foreach($fields as$field){if(is_object($field))$field=$field->__toString();echo'';}echo'';}echo'
Row'.htmlSpecialChars($info['name']).'
',$row,'',htmlSpecialChars($field),'
';}} + class DibiMySqlDriver extends DibiDriver{private$conn;public$formats=array('NULL'=>"NULL",'TRUE'=>"1",'FALSE'=>"0",'date'=>"'Y-m-d'",'datetime'=>"'Y-m-d H:i:s'",);public static function connect($config){if(!extension_loaded('mysql'))return new DibiException("PHP extension 'mysql' is not loaded");if(empty($config['host']))$config['host']='localhost';if(@$config['protocol']==='unix')$host=':'.$config['host'];else$host=$config['host'].(empty($config['port'])?'':$config['port']);if(function_exists('ini_set'))$save=ini_set('track_errors',TRUE);$php_errormsg='';if(empty($config['persistent']))$conn=@mysql_connect($host,@$config['username'],@$config['password']);else$conn=@mysql_pconnect($host,@$config['username'],@$config['password']);if(function_exists('ini_set'))ini_set('track_errors',$save);if(!is_resource($conn))return new DibiException("Connecting error",array('message'=>mysql_error()?mysql_error():$php_errormsg,'code'=>mysql_errno(),));if(!empty($config['charset'])){$succ=@mysql_query('SET CHARACTER SET '.$config['charset'],$conn);}if(!empty($config['database'])){if(!@mysql_select_db($config['database'],$conn))return new DibiException("Connecting error",array('message'=>mysql_error($conn),'code'=>mysql_errno($conn),));}$obj=new self($config);$obj->conn=$conn;return$obj;}public function query($sql){$res=@mysql_query($sql,$this->conn);if(is_resource($res))return new DibiMySqlResult($res);if($res===FALSE)return new DibiException("Query error",array('message'=>mysql_error($this->conn),'code'=>mysql_errno($this->conn),'sql'=>$sql,));return TRUE;}public function affectedRows(){$rows=mysql_affected_rows($this->conn);return$rows<0?FALSE:$rows;}public function insertId(){$id=mysql_insert_id($this->conn);return$id<0?FALSE:$id;}public function begin(){return mysql_query('BEGIN',$this->conn);}public function commit(){return mysql_query('COMMIT',$this->conn);}public function rollback(){return mysql_query('ROLLBACK',$this->conn);}public function escape($value,$appendQuotes=FALSE){return$appendQuotes?"'".mysql_real_escape_string($value,$this->conn)."'":mysql_real_escape_string($value,$this->conn);}public function quoteName($value){return'`'.strtr($value,array('.'=>'`.`')).'`';}public function getMetaData(){trigger_error('Meta is not implemented yet.',E_USER_WARNING);}}class DibiMySqlResult extends DibiResult{private$resource,$meta;public function __construct($resource){$this->resource=$resource;}public function rowCount(){return mysql_num_rows($this->resource);}protected function doFetch(){return mysql_fetch_assoc($this->resource);}public function seek($row){return mysql_data_seek($this->resource,$row);}protected function free(){mysql_free_result($this->resource);}public function getFields(){if($this->meta===NULL)$this->createMeta();return array_keys($this->meta);}protected function detectTypes(){if($this->meta===NULL)$this->createMeta();}public function getMetaData($field){if($this->meta===NULL)$this->createMeta();return isset($this->meta[$field])?$this->meta[$field]:FALSE;}private function createMeta(){static$types=array('ENUM'=>self::FIELD_TEXT,'SET'=>self::FIELD_TEXT,'CHAR'=>self::FIELD_TEXT,'VARCHAR'=>self::FIELD_TEXT,'STRING'=>self::FIELD_TEXT,'TINYTEXT'=>self::FIELD_TEXT,'TEXT'=>self::FIELD_TEXT,'MEDIUMTEXT'=>self::FIELD_TEXT,'LONGTEXT'=>self::FIELD_TEXT,'BINARY'=>self::FIELD_BINARY,'VARBINARY'=>self::FIELD_BINARY,'TINYBLOB'=>self::FIELD_BINARY,'BLOB'=>self::FIELD_BINARY,'MEDIUMBLOB'=>self::FIELD_BINARY,'LONGBLOB'=>self::FIELD_BINARY,'DATE'=>self::FIELD_DATE,'DATETIME'=>self::FIELD_DATETIME,'TIMESTAMP'=>self::FIELD_DATETIME,'TIME'=>self::FIELD_DATETIME,'BIT'=>self::FIELD_BOOL,'YEAR'=>self::FIELD_INTEGER,'TINYINT'=>self::FIELD_INTEGER,'SMALLINT'=>self::FIELD_INTEGER,'MEDIUMINT'=>self::FIELD_INTEGER,'INT'=>self::FIELD_INTEGER,'INTEGER'=>self::FIELD_INTEGER,'BIGINT'=>self::FIELD_INTEGER,'FLOAT'=>self::FIELD_FLOAT,'DOUBLE'=>self::FIELD_FLOAT,'REAL'=>self::FIELD_FLOAT,'DECIMAL'=>self::FIELD_FLOAT,'NUMERIC'=>self::FIELD_FLOAT,);$count=mysql_num_fields($this->resource);$this->meta=$this->convert=array();for($index=0;$index<$count;$index++){$info['native']=$native=strtoupper(mysql_field_type($this->resource,$index));$info['flags']=explode(' ',mysql_field_flags($this->resource,$index));$info['length']=mysql_field_len($this->resource,$index);$info['table']=mysql_field_table($this->resource,$index);if(in_array('auto_increment',$info['flags']))$info['type']=self::FIELD_COUNTER;else{$info['type']=isset($types[$native])?$types[$native]:self::FIELD_UNKNOWN;}$name=mysql_field_name($this->resource,$index);$this->meta[$name]=$info;$this->convert[$name]=$info['type'];}}} + class DibiMySqliDriver extends DibiDriver{private$conn;public$formats=array('NULL'=>"NULL",'TRUE'=>"1",'FALSE'=>"0",'date'=>"'Y-m-d'",'datetime'=>"'Y-m-d H:i:s'",);public static function connect($config){if(!extension_loaded('mysqli'))return new DibiException("PHP extension 'mysqli' is not loaded");if(empty($config['host']))$config['host']='localhost';$conn=@mysqli_connect($config['host'],@$config['username'],@$config['password'],@$config['database'],@$config['port']);if(!$conn)return new DibiException("Connecting error",array('message'=>mysqli_connect_error(),'code'=>mysqli_connect_errno(),));if(!empty($config['charset']))mysqli_query($conn,'SET CHARACTER SET '.$config['charset']);$obj=new self($config);$obj->conn=$conn;return$obj;}public function query($sql){$res=@mysqli_query($this->conn,$sql);if(is_object($res))return new DibiMySqliResult($res);if($res===FALSE)return new DibiException("Query error",$this->errorInfo($sql));return TRUE;}public function affectedRows(){$rows=mysqli_affected_rows($this->conn);return$rows<0?FALSE:$rows;}public function insertId(){$id=mysqli_insert_id($this->conn);return$id<1?FALSE:$id;}public function begin(){return mysqli_autocommit($this->conn,FALSE);}public function commit(){$ok=mysqli_commit($this->conn);mysqli_autocommit($this->conn,TRUE);return$ok;}public function rollback(){$ok=mysqli_rollback($this->conn);mysqli_autocommit($this->conn,TRUE);return$ok;}private function errorInfo($sql=NULL){return array('message'=>mysqli_error($this->conn),'code'=>mysqli_errno($this->conn),'sql'=>$sql,);}public function escape($value,$appendQuotes=FALSE){return$appendQuotes?"'".mysqli_real_escape_string($this->conn,$value)."'":mysqli_real_escape_string($this->conn,$value);}public function quoteName($value){return'`'.strtr($value,array('.'=>'`.`')).'`';}public function getMetaData(){trigger_error('Meta is not implemented yet.',E_USER_WARNING);}}class DibiMySqliResult extends DibiResult{private$resource,$meta;public function __construct($resource){$this->resource=$resource;}public function rowCount(){return mysqli_num_rows($this->resource);}protected function doFetch(){return mysqli_fetch_assoc($this->resource);}public function seek($row){return mysqli_data_seek($this->resource,$row);}protected function free(){mysqli_free_result($this->resource);}public function getFields(){if($this->meta===NULL)$this->createMeta();return array_keys($this->meta);}protected function detectTypes(){if($this->meta===NULL)$this->createMeta();}public function getMetaData($field){if($this->meta===NULL)$this->createMeta();return isset($this->meta[$field])?$this->meta[$field]:FALSE;}private function createMeta(){static$types=array(MYSQLI_TYPE_FLOAT=>self::FIELD_FLOAT,MYSQLI_TYPE_DOUBLE=>self::FIELD_FLOAT,MYSQLI_TYPE_DECIMAL=>self::FIELD_FLOAT,MYSQLI_TYPE_TINY=>self::FIELD_INTEGER,MYSQLI_TYPE_SHORT=>self::FIELD_INTEGER,MYSQLI_TYPE_LONG=>self::FIELD_INTEGER,MYSQLI_TYPE_LONGLONG=>self::FIELD_INTEGER,MYSQLI_TYPE_INT24=>self::FIELD_INTEGER,MYSQLI_TYPE_YEAR=>self::FIELD_INTEGER,MYSQLI_TYPE_GEOMETRY=>self::FIELD_INTEGER,MYSQLI_TYPE_DATE=>self::FIELD_DATE,MYSQLI_TYPE_NEWDATE=>self::FIELD_DATE,MYSQLI_TYPE_TIMESTAMP=>self::FIELD_DATETIME,MYSQLI_TYPE_TIME=>self::FIELD_DATETIME,MYSQLI_TYPE_DATETIME=>self::FIELD_DATETIME,MYSQLI_TYPE_ENUM=>self::FIELD_TEXT,MYSQLI_TYPE_SET=>self::FIELD_TEXT,MYSQLI_TYPE_STRING=>self::FIELD_TEXT,MYSQLI_TYPE_VAR_STRING=>self::FIELD_TEXT,MYSQLI_TYPE_TINY_BLOB=>self::FIELD_BINARY,MYSQLI_TYPE_MEDIUM_BLOB=>self::FIELD_BINARY,MYSQLI_TYPE_LONG_BLOB=>self::FIELD_BINARY,MYSQLI_TYPE_BLOB=>self::FIELD_BINARY,);$count=mysqli_num_fields($this->resource);$this->meta=$this->convert=array();for($index=0;$index<$count;$index++){$info=(array)mysqli_fetch_field_direct($this->resource,$index);$native=$info['native']=$info['type'];if($info['flags']&MYSQLI_AUTO_INCREMENT_FLAG)$info['type']=self::FIELD_COUNTER;else{$info['type']=isset($types[$native])?$types[$native]:self::FIELD_UNKNOWN;}$this->meta[$info['name']]=$info;$this->convert[$info['name']]=$info['type'];}}} + class DibiOdbcDriver extends DibiDriver{private$conn;public$formats=array('NULL'=>"NULL",'TRUE'=>"-1",'FALSE'=>"0",'date'=>"#m/d/Y#",'datetime'=>"#m/d/Y H:i:s#",);public static function connect($config){if(!extension_loaded('odbc'))return new DibiException("PHP extension 'odbc' is not loaded");if(@$config['persistent'])$conn=@odbc_pconnect($config['database'],$config['username'],$config['password']);else$conn=@odbc_connect($config['database'],$config['username'],$config['password']);if(!is_resource($conn))return new DibiException("Connecting error",array('message'=>odbc_errormsg(),'code'=>odbc_error(),));$obj=new self($config);$obj->conn=$conn;return$obj;}public function query($sql){$res=@odbc_exec($this->conn,$sql);if(is_resource($res))return new DibiOdbcResult($res);if($res===FALSE)return new DibiException("Query error",$this->errorInfo($sql));return TRUE;}public function affectedRows(){$rows=odbc_num_rows($this->conn);return$rows<0?FALSE:$rows;}public function insertId(){return FALSE;}public function begin(){return odbc_autocommit($this->conn,FALSE);}public function commit(){$ok=odbc_commit($this->conn);odbc_autocommit($this->conn,TRUE);return$ok;}public function rollback(){$ok=odbc_rollback($this->conn);odbc_autocommit($this->conn,TRUE);return$ok;}private function errorInfo($sql=NULL){return array('message'=>odbc_errormsg($this->conn),'code'=>odbc_error($this->conn),'sql'=>$sql,);}public function escape($value,$appendQuotes=FALSE){$value=str_replace("'","''",$value);return$appendQuotes?"'".$value."'":$value;}public function quoteName($value){return'['.strtr($value,array('.'=>'].[')).']';}public function getMetaData(){trigger_error('Meta is not implemented yet.',E_USER_WARNING);}}class DibiOdbcResult extends DibiResult{private$resource,$meta,$row=0;public function __construct($resource){$this->resource=$resource;}public function rowCount(){return odbc_num_rows($this->resource);}protected function doFetch(){return odbc_fetch_array($this->resource,$this->row++);}public function seek($row){$this->row=$row;}protected function free(){odbc_free_result($this->resource);}public function getFields(){if($this->meta===NULL)$this->createMeta();return array_keys($this->meta);}protected function detectTypes(){if($this->meta===NULL)$this->createMeta();}public function getMetaData($field){if($this->meta===NULL)$this->createMeta();return isset($this->meta[$field])?$this->meta[$field]:FALSE;}private function createMeta(){if($this->meta!==NULL)return$this->meta;static$types=array('CHAR'=>self::FIELD_TEXT,'COUNTER'=>self::FIELD_COUNTER,'VARCHAR'=>self::FIELD_TEXT,'LONGCHAR'=>self::FIELD_TEXT,'INTEGER'=>self::FIELD_INTEGER,'DATETIME'=>self::FIELD_DATETIME,'CURRENCY'=>self::FIELD_FLOAT,'BIT'=>self::FIELD_BOOL,'LONGBINARY'=>self::FIELD_BINARY,'SMALLINT'=>self::FIELD_INTEGER,'BYTE'=>self::FIELD_INTEGER,'BIGINT'=>self::FIELD_INTEGER,'INT'=>self::FIELD_INTEGER,'TINYINT'=>self::FIELD_INTEGER,'REAL'=>self::FIELD_FLOAT,'DOUBLE'=>self::FIELD_FLOAT,'DECIMAL'=>self::FIELD_FLOAT,'NUMERIC'=>self::FIELD_FLOAT,'MONEY'=>self::FIELD_FLOAT,'SMALLMONEY'=>self::FIELD_FLOAT,'FLOAT'=>self::FIELD_FLOAT,'YESNO'=>self::FIELD_BOOL,);$count=odbc_num_fields($this->resource);$this->meta=$this->convert=array();for($index=1;$index<=$count;$index++){$native=strtoupper(odbc_field_type($this->resource,$index));$name=odbc_field_name($this->resource,$index);$this->meta[$name]=array('type'=>isset($types[$native])?$types[$native]:self::FIELD_UNKNOWN,'native'=>$native,'length'=>odbc_field_len($this->resource,$index),'scale'=>odbc_field_scale($this->resource,$index),'precision'=>odbc_field_precision($this->resource,$index),);$this->convert[$name]=$this->meta[$name]['type'];}}} + class DibiSqliteDriver extends DibiDriver{private$conn;public$formats=array('NULL'=>"NULL",'TRUE'=>"1",'FALSE'=>"0",'date'=>"'Y-m-d'",'datetime'=>"'Y-m-d H:i:s'",);public static function connect($config){if(!extension_loaded('sqlite'))return new DibiException("PHP extension 'sqlite' is not loaded");if(empty($config['database']))return new DibiException("Database must be specified");$errorMsg='';if(empty($config['persistent']))$conn=@sqlite_open($config['database'],@$config['mode'],$errorMsg);else$conn=@sqlite_popen($config['database'],@$config['mode'],$errorMsg);if(!$conn)return new DibiException("Connecting error",array('message'=>$errorMsg,));$obj=new self($config);$obj->conn=$conn;return$obj;}public function query($sql){$errorMsg='';$res=@sqlite_query($this->conn,$sql,SQLITE_ASSOC,$errorMsg);if($res===FALSE)return new DibiException("Query error",array('message'=>$errorMsg,'sql'=>$sql,));if(is_resource($res))return new DibiSqliteResult($res);return TRUE;}public function affectedRows(){$rows=sqlite_changes($this->conn);return$rows<0?FALSE:$rows;}public function insertId(){$id=sqlite_last_insert_rowid($this->conn);return$id<1?FALSE:$id;}public function begin(){return sqlite_query($this->conn,'BEGIN');}public function commit(){return sqlite_query($this->conn,'COMMIT');}public function rollback(){return sqlite_query($this->conn,'ROLLBACK');}public function escape($value,$appendQuotes=FALSE){return$appendQuotes?"'".sqlite_escape_string($value)."'":sqlite_escape_string($value);}public function quoteName($value){return$value;}public function getMetaData(){trigger_error('Meta is not implemented yet.',E_USER_WARNING);}}class DibiSqliteResult extends DibiResult{private$resource,$meta;public function __construct($resource){$this->resource=$resource;}public function rowCount(){return sqlite_num_rows($this->resource);}protected function doFetch(){return sqlite_fetch_array($this->resource,SQLITE_ASSOC);}public function seek($row){return sqlite_seek($this->resource,$row);}protected function free(){}public function getFields(){if($this->meta===NULL)$this->createMeta();return array_keys($this->meta);}protected function detectTypes(){if($this->meta===NULL)$this->createMeta();}public function getMetaData($field){if($this->meta===NULL)$this->createMeta();return isset($this->meta[$field])?$this->meta[$field]:FALSE;}private function createMeta(){$count=sqlite_num_fields($this->resource);$this->meta=$this->convert=array();for($index=0;$index<$count;$index++){$name=sqlite_field_name($this->resource,$index);$this->meta[$name]=array('type'=>self::FIELD_UNKNOWN);$this->convert[$name]=self::FIELD_UNKNOWN;}}}?> \ No newline at end of file diff --git a/dibi/dibi.php b/dibi/dibi.php new file mode 100644 index 00000000..f43f6f57 --- /dev/null +++ b/dibi/dibi.php @@ -0,0 +1,416 @@ + + * @link http://texy.info/dibi/ + * @copyright Copyright (c) 2005-2006 David Grudl + * @license GNU GENERAL PUBLIC LICENSE + * @package dibi + * @category Database + * @version 0.5alpha (2006-05-26) for PHP5 + */ + + +define('dibi', 'Database Abstraction Layer (c) David Grudl, http://texy.info/dibi/'); + + +if (version_compare(PHP_VERSION , '5.0.3', '<')) + die('dibi needs PHP 5.0.3 or newer'); + + +// libraries +require_once dirname(__FILE__).'/libs/driver.php'; +require_once dirname(__FILE__).'/libs/resultset.php'; +require_once dirname(__FILE__).'/libs/parser.php'; +require_once dirname(__FILE__).'/libs/exception.php'; + +// support +require_once dirname(__FILE__).'/libs/date.type.demo.php'; + + + + +/** + * Interface for user variable, used for generating SQL + */ +interface IDibiVariable +{ + /** + * Format for SQL + * + * @param object destination DibiDriver + * @param string optional modifier + * @return string SQL code + */ + public function toSQL($driver, $modifier = NULL); +} + + + + + +/** + * Interface for database drivers + * + * This class is static container class for creating DB objects and + * store debug & connections info. + * + */ +class dibi +{ + /** + * Connection registry storage for DibiDriver objects + * @var array + */ + static private $registry = array(); + + /** + * Current connection + * @var object DibiDriver + */ + static private $conn; + + /** + * Arguments -> SQL parser + * @var object DibiParser + */ + static private $parser; + + /** + * Last SQL command @see dibi::query() + * @var string + */ + static public $sql; + static public $error; + + /** + * File for logging SQL queryies - strongly recommended to use with NSafeStream + * @var string|NULL + */ + static public $logfile; + + /** + * Enable/disable debug mode + * @var bool + */ + static public $debug = false; + + /** + * Progressive created query + * @var array + */ + static private $query = array(); + + + + /** + * Creates a new DibiDriver object and connects it to specified database + * + * @param array connection parameters + * @param string connection name + * @return bool|object TRUE on success, FALSE or Exception on failure + */ + static public function connect($config, $name = 'def') + { + // init parser + if (!self::$parser) self::$parser = new DibiParser(); + + // $name must be unique + if (isset(self::$registry[$name])) + return new DibiException("Connection named '$name' already exists."); + + // config['driver'] is required + if (empty($config['driver'])) + return new DibiException('Driver is not specified.'); + + // include dibi driver + $className = "Dibi$config[driver]Driver"; + require_once dirname(__FILE__) . "/drivers/$config[driver].php"; + + if (!class_exists($className)) + return new DibiException("Unable to create instance of dibi driver class '$className'."); + + + // create connection object + /** like $conn = $className::connect($config); */ + $conn = call_user_func(array($className, 'connect'), $config); + + // optionally log to file + // todo: log other exceptions! + if (self::$logfile != NULL) { + if (is_error($conn)) + $msg = "Can't connect to DB '$config[driver]': ".$conn->getMessage(); + else + $msg = "Successfully connected to DB '$config[driver]'"; + + $f = fopen(self::$logfile, 'a'); + fwrite($f, "$msg\r\n\r\n"); + fclose($f); + } + + if (is_error($conn)) { + // optionally debug on display + if (self::$debug) echo '[dibi error] ' . $conn->getMessage(); + + return $conn; // reraise the exception + } + + // store connection in list + self::$conn = self::$registry[$name] = $conn; + + return TRUE; + } + + + + /** + * Returns TRUE when connection was established + * + * @return bool + */ + static public function isConnected() + { + return (bool) self::$conn; + } + + + /** + * Retrieve active connection + * + * @param string connection registy name or NULL for active connection + * @return object DibiDriver object. + */ + static public function getConnection($name = NULL) + { + return $name === NULL + ? self::$conn + : @self::$registry[$name]; + } + + + /** + * Change active connection + * + * @param string connection registy name + * @return void + */ + static public function activate($name) + { + if (!isset(self::$registry[$name])) + return FALSE; + + // change active connection + self::$conn = self::$registry[$name]; + return TRUE; + } + + + + + + + /** + * Generates and executes SQL query + * + * @param mixed one or more arguments + * @return int|DibiResult|Exception + */ + static public function query() + { + if (!self::$conn) return new DibiException('Dibi is not connected to DB'); // is connected? + + // receive arguments + $args = func_num_args() ? func_get_args() : self::$query; + self::$query = array(); + + // and generate SQL + self::$sql = self::$parser->parse(self::$conn, $args); + if (is_error(self::$sql)) return self::$sql; // reraise the exception + + // execute SQL + $timer = -microtime(true); + $res = self::$conn->query(self::$sql); + $timer += microtime(true); + + if (is_error($res)) { + // optionally debug on display + if (self::$debug) { + echo '[dibi error] ' . $res->getMessage(); + self::dump(self::$sql); + } + // todo: log all errors! + self::$error = $res; + } else { + self::$error = FALSE; + } + + // optionally log to file + if (self::$logfile != NULL) + { + if (is_error($res)) + $msg = $res->getMessage(); + elseif ($res instanceof DibiResult) + $msg = 'object('.get_class($res).') rows: '.$res->rowCount(); + else + $msg = 'OK'; + + $f = fopen(self::$logfile, 'a'); + fwrite($f, + self::$sql + . ";\r\n-- Result: $msg" + . "\r\n-- Takes: " . sprintf('%0.3f', $timer * 1000) . ' ms' + . "\r\n\r\n" + ); + fclose($f); + } + + return $res; + } + + + static public function queryStart() + { + self::$query = func_get_args(); + } + + + static public function queryAdd() + { + $args = func_get_args(); + self::$query = array_merge(self::$query, $args); + } + + + + /** + * Generates and returns SQL query + * + * @param mixed one or more arguments + * @return string + */ + static public function test() + { + if (!self::$conn) return FALSE; // is connected? + + // receive arguments + $args = func_num_args() ? func_get_args() : self::$query; + self::$query = array(); + + // and generate SQL + $sql = self::$parser->parse(self::$conn, $args); + if (is_error($sql)) { + self::dump($sql->getSql()); + return $sql->getSql(); + } else { + self::dump($sql); + return $sql; + } + } + + + + /** + * Monostate for DibiDriver::insertId() + * + * @return int + */ + static public function insertId() + { + if (!self::$conn) return FALSE; // is connected? + + return self::$conn->insertId(); + } + + + + /** + * Monostate for DibiDriver::affectedRows() + * + * @return int + */ + static public function affectedRows() + { + if (!self::$conn) return FALSE; // is connected? + + return self::$conn->affectedRows(); + } + + + + /** + * Prints out a syntax highlighted version of the SQL command + * + * @param string SQL command + * @return void + */ + static public function dump($sql) { + static $highlight = array ('ALL', 'DISTINCT', 'AS', 'ON', 'INTO', 'AND', 'OR', 'AS', ); + static $newline = array ('SELECT', 'UPDATE', 'INSERT', 'DELETE', 'FROM', 'WHERE', 'HAVING', 'GROUP BY', 'ORDER BY', 'LIMIT', 'SET', 'VALUES', 'LEFT JOIN', 'INNER JOIN',); + + // insert new lines + foreach ($newline as $word) + $sql = preg_replace('#\b'.$word.'\b#', "\n\$0", $sql); + + $sql = trim($sql); + // reduce spaces + // $sql = preg_replace('# +#', ' ', $sql); + + $sql = wordwrap($sql, 100); + $sql = htmlSpecialChars($sql); + $sql = strtr($sql, array("\n" => '
')); + + foreach ($newline as $word) + $sql = preg_replace('#\b'.$word.'\b#', '$0', $sql); + + foreach ($highlight as $word) + $sql = preg_replace('#\b'.$word.'\b#', '$0', $sql); + + $sql = preg_replace('#\*\*.+?\*\*#', '$0', $sql); + + echo '
', $sql, '
'; + } + + + + /** + * Displays complete result-set as HTML table + * + * @param object DibiResult + * @return void + */ + static public function dumpResult(DibiResult $res) + { + echo ''; + echo ''; + $fieldCount = $res->fieldCount(); + for ($i = 0; $i < $fieldCount; $i++) { + $info = $res->fieldMeta($i); + echo ''; + } + echo ''; + + foreach ($res as $row => $fields) { + echo ''; + foreach ($fields as $field) { + if (is_object($field)) $field = $field->__toString(); + echo ''; + } + echo ''; + } + echo '
Row'.htmlSpecialChars($info['name']).'
', $row, '', htmlSpecialChars($field), '
'; + } + + + +} // class dibi + + + + + +?> \ No newline at end of file diff --git a/dibi/drivers/mysql.php b/dibi/drivers/mysql.php new file mode 100644 index 00000000..56b24388 --- /dev/null +++ b/dibi/drivers/mysql.php @@ -0,0 +1,334 @@ + + * @link http://texy.info/dibi/ + * @copyright Copyright (c) 2005-2006 David Grudl + * @license GNU GENERAL PUBLIC LICENSE + * @package dibi + * @category Database + * @version 0.5alpha (2006-05-26) for PHP5 + */ + + +// security - include dibi.php, not this file +if (!defined('dibi')) die(); + + +/** + * The dibi driver for MySQL database + * + */ +class DibiMySqlDriver extends DibiDriver { + private + $conn; + + public + $formats = array( + 'NULL' => "NULL", + 'TRUE' => "1", + 'FALSE' => "0", + 'date' => "'Y-m-d'", + 'datetime' => "'Y-m-d H:i:s'", + ); + + + /** + * Driver factory + */ + public static function connect($config) + { + if (!extension_loaded('mysql')) + return new DibiException("PHP extension 'mysql' is not loaded"); + + + if (empty($config['host'])) $config['host'] = 'localhost'; + + if (@$config['protocol'] === 'unix') // host can be socket + $host = ':' . $config['host']; + else + $host = $config['host'] . (empty($config['port']) ? '' : $config['port']); + + + // some errors aren't handled. Must use $php_errormsg + if (function_exists('ini_set')) + $save = ini_set('track_errors', TRUE); + $php_errormsg = ''; + + if (empty($config['persistent'])) + $conn = @mysql_connect($host, @$config['username'], @$config['password']); + else + $conn = @mysql_pconnect($host, @$config['username'], @$config['password']); + + if (function_exists('ini_set')) + ini_set('track_errors', $save); + + + if (!is_resource($conn)) + return new DibiException("Connecting error", array( + 'message' => mysql_error() ? mysql_error() : $php_errormsg, + 'code' => mysql_errno(), + )); + + + if (!empty($config['charset'])) { + $succ = @mysql_query('SET CHARACTER SET '.$config['charset'], $conn); + // don't handle this error... + } + + + if (!empty($config['database'])) { + if (!@mysql_select_db($config['database'], $conn)) + return new DibiException("Connecting error", array( + 'message' => mysql_error($conn), + 'code' => mysql_errno($conn), + )); + } + + + $obj = new self($config); + $obj->conn = $conn; + return $obj; + } + + + + public function query($sql) + { + $res = @mysql_query($sql, $this->conn); + + if (is_resource($res)) + return new DibiMySqlResult($res); + + if ($res === FALSE) + return new DibiException("Query error", array( + 'message' => mysql_error($this->conn), + 'code' => mysql_errno($this->conn), + 'sql' => $sql, + )); + + return TRUE; + } + + + public function affectedRows() + { + $rows = mysql_affected_rows($this->conn); + return $rows < 0 ? FALSE : $rows; + } + + + public function insertId() + { + $id = mysql_insert_id($this->conn); + return $id < 0 ? FALSE : $id; + } + + + public function begin() + { + return mysql_query('BEGIN', $this->conn); + } + + + public function commit() + { + return mysql_query('COMMIT', $this->conn); + } + + + public function rollback() + { + return mysql_query('ROLLBACK', $this->conn); + } + + + public function escape($value, $appendQuotes = FALSE) + { + return $appendQuotes + ? "'" . mysql_real_escape_string($value, $this->conn) . "'" + : mysql_real_escape_string($value, $this->conn); + } + + + public function quoteName($value) + { + return '`' . strtr($value, array('.' => '`.`')) . '`'; + } + + + public function getMetaData() + { + trigger_error('Meta is not implemented yet.', E_USER_WARNING); + } + + +/* + // is this really needed? + public function getResource() + { + return $this->conn; + } + + // experimental + public function applyLimit(&$sql, $offset, $limit) + { + if ($limit > 0) { + $sql .= " LIMIT " . (int) $limit . ($offset > 0 ? " OFFSET " . (int) $offset : ""); + } elseif ($offset > 0) { + $sql .= " LIMIT " . $offset . ", 18446744073709551615"; + } + } +*/ + +} // DibiMySqlDriver + + + + + + + + + +class DibiMySqlResult extends DibiResult +{ + private + $resource, + $meta; + + + public function __construct($resource) + { + $this->resource = $resource; + } + + + public function rowCount() + { + return mysql_num_rows($this->resource); + } + + + protected function doFetch() + { + return mysql_fetch_assoc($this->resource); + } + + + public function seek($row) + { + return mysql_data_seek($this->resource, $row); + } + + + protected function free() + { + mysql_free_result($this->resource); + } + + + public function getFields() + { + // cache + if ($this->meta === NULL) + $this->createMeta(); + + return array_keys($this->meta); + } + + + protected function detectTypes() + { + if ($this->meta === NULL) + $this->createMeta(); + } + + + /** this is experimental */ + public function getMetaData($field) + { + // cache + if ($this->meta === NULL) + $this->createMeta(); + + return isset($this->meta[$field]) ? $this->meta[$field] : FALSE; + } + + + + /** this is experimental */ + private function createMeta() + { + static $types = array( + 'ENUM' => self::FIELD_TEXT, // eventually self::FIELD_INTEGER + 'SET' => self::FIELD_TEXT, // eventually self::FIELD_INTEGER + 'CHAR' => self::FIELD_TEXT, + 'VARCHAR' => self::FIELD_TEXT, + 'STRING' => self::FIELD_TEXT, + 'TINYTEXT' => self::FIELD_TEXT, + 'TEXT' => self::FIELD_TEXT, + 'MEDIUMTEXT'=> self::FIELD_TEXT, + 'LONGTEXT' => self::FIELD_TEXT, + 'BINARY' => self::FIELD_BINARY, + 'VARBINARY' => self::FIELD_BINARY, + 'TINYBLOB' => self::FIELD_BINARY, + 'BLOB' => self::FIELD_BINARY, + 'MEDIUMBLOB'=> self::FIELD_BINARY, + 'LONGBLOB' => self::FIELD_BINARY, + 'DATE' => self::FIELD_DATE, + 'DATETIME' => self::FIELD_DATETIME, + 'TIMESTAMP' => self::FIELD_DATETIME, + 'TIME' => self::FIELD_DATETIME, + 'BIT' => self::FIELD_BOOL, + 'YEAR' => self::FIELD_INTEGER, + 'TINYINT' => self::FIELD_INTEGER, + 'SMALLINT' => self::FIELD_INTEGER, + 'MEDIUMINT' => self::FIELD_INTEGER, + 'INT' => self::FIELD_INTEGER, + 'INTEGER' => self::FIELD_INTEGER, + 'BIGINT' => self::FIELD_INTEGER, + 'FLOAT' => self::FIELD_FLOAT, + 'DOUBLE' => self::FIELD_FLOAT, + 'REAL' => self::FIELD_FLOAT, + 'DECIMAL' => self::FIELD_FLOAT, + 'NUMERIC' => self::FIELD_FLOAT, + ); + + $count = mysql_num_fields($this->resource); + $this->meta = $this->convert = array(); + for ($index = 0; $index < $count; $index++) { + + $info['native'] = $native = strtoupper(mysql_field_type($this->resource, $index)); + $info['flags'] = explode(' ', mysql_field_flags($this->resource, $index)); + $info['length'] = mysql_field_len($this->resource, $index); + $info['table'] = mysql_field_table($this->resource, $index); + + if (in_array('auto_increment', $info['flags'])) // or 'primary_key' ? + $info['type'] = self::FIELD_COUNTER; + else { + $info['type'] = isset($types[$native]) ? $types[$native] : self::FIELD_UNKNOWN; + +// if ($info['type'] == self::FIELD_TEXT && $info['length'] > 255) +// $info['type'] = self::FIELD_LONG_TEXT; + } + + $name = mysql_field_name($this->resource, $index); + $this->meta[$name] = $info; + $this->convert[$name] = $info['type']; + } + } + + +} // class DibiMySqlResult + + + + + +?> \ No newline at end of file diff --git a/dibi/drivers/mysqli.php b/dibi/drivers/mysqli.php new file mode 100644 index 00000000..92311090 --- /dev/null +++ b/dibi/drivers/mysqli.php @@ -0,0 +1,282 @@ + + * @link http://texy.info/dibi/ + * @copyright Copyright (c) 2005-2006 David Grudl + * @license GNU GENERAL PUBLIC LICENSE + * @package dibi + * @category Database + * @version 0.5alpha (2006-05-26) for PHP5 + */ + + +// security - include dibi.php, not this file +if (!defined('dibi')) die(); + + +/** + * The dibi driver for MySQLi database + * + */ +class DibiMySqliDriver extends DibiDriver { + private + $conn; + + public + $formats = array( + 'NULL' => "NULL", + 'TRUE' => "1", + 'FALSE' => "0", + 'date' => "'Y-m-d'", + 'datetime' => "'Y-m-d H:i:s'", + ); + + + + public static function connect($config) + { + if (!extension_loaded('mysqli')) + return new DibiException("PHP extension 'mysqli' is not loaded"); + + if (empty($config['host'])) $config['host'] = 'localhost'; + + $conn = @mysqli_connect($config['host'], @$config['username'], @$config['password'], @$config['database'], @$config['port']); + + if (!$conn) + return new DibiException("Connecting error", array( + 'message' => mysqli_connect_error(), + 'code' => mysqli_connect_errno(), + )); + + if (!empty($config['charset'])) + mysqli_query($conn, 'SET CHARACTER SET '.$config['charset']); + + $obj = new self($config); + $obj->conn = $conn; + return $obj; + } + + + + public function query($sql) + { + $res = @mysqli_query($this->conn, $sql); + + if (is_object($res)) + return new DibiMySqliResult($res); + + if ($res === FALSE) + return new DibiException("Query error", $this->errorInfo($sql)); + + return TRUE; + } + + + public function affectedRows() + { + $rows = mysqli_affected_rows($this->conn); + return $rows < 0 ? FALSE : $rows; + } + + + public function insertId() + { + $id = mysqli_insert_id($this->conn); + return $id < 1 ? FALSE : $id; + } + + + public function begin() + { + return mysqli_autocommit($this->conn, FALSE); + } + + + public function commit() + { + $ok = mysqli_commit($this->conn); + mysqli_autocommit($this->conn, TRUE); + return $ok; + } + + + public function rollback() + { + $ok = mysqli_rollback($this->conn); + mysqli_autocommit($this->conn, TRUE); + return $ok; + } + + + private function errorInfo($sql = NULL) + { + return array( + 'message' => mysqli_error($this->conn), + 'code' => mysqli_errno($this->conn), + 'sql' => $sql, + ); + } + + + + + public function escape($value, $appendQuotes = FALSE) + { + return $appendQuotes + ? "'" . mysqli_real_escape_string($this->conn, $value) . "'" + : mysqli_real_escape_string($this->conn, $value); + } + + + public function quoteName($value) + { + return '`' . strtr($value, array('.' => '`.`')) . '`'; + } + + + + public function getMetaData() + { + trigger_error('Meta is not implemented yet.', E_USER_WARNING); + } + + +} // class DibiMySqliDriver + + + + + + + + + +class DibiMySqliResult extends DibiResult +{ + private + $resource, + $meta; + + + public function __construct($resource) + { + $this->resource = $resource; + } + + + public function rowCount() + { + return mysqli_num_rows($this->resource); + } + + + protected function doFetch() + { + return mysqli_fetch_assoc($this->resource); + } + + + public function seek($row) + { + return mysqli_data_seek($this->resource, $row); + } + + + protected function free() + { + mysqli_free_result($this->resource); + } + + + public function getFields() + { + // cache + if ($this->meta === NULL) + $this->createMeta(); + + return array_keys($this->meta); + } + + + protected function detectTypes() + { + if ($this->meta === NULL) + $this->createMeta(); + } + + /** this is experimental */ + public function getMetaData($field) + { + // cache + if ($this->meta === NULL) + $this->createMeta(); + + return isset($this->meta[$field]) ? $this->meta[$field] : FALSE; + } + + + + /** this is experimental */ + private function createMeta() + { + static $types = array( + MYSQLI_TYPE_FLOAT => self::FIELD_FLOAT, + MYSQLI_TYPE_DOUBLE => self::FIELD_FLOAT, + MYSQLI_TYPE_DECIMAL => self::FIELD_FLOAT, + // MYSQLI_TYPE_NEWDECIMAL=> self::FIELD_FLOAT, + // MYSQLI_TYPE_BIT => self::FIELD_INTEGER, + MYSQLI_TYPE_TINY => self::FIELD_INTEGER, + MYSQLI_TYPE_SHORT => self::FIELD_INTEGER, + MYSQLI_TYPE_LONG => self::FIELD_INTEGER, + MYSQLI_TYPE_LONGLONG => self::FIELD_INTEGER, + MYSQLI_TYPE_INT24 => self::FIELD_INTEGER, + MYSQLI_TYPE_YEAR => self::FIELD_INTEGER, + MYSQLI_TYPE_GEOMETRY => self::FIELD_INTEGER, + MYSQLI_TYPE_DATE => self::FIELD_DATE, + MYSQLI_TYPE_NEWDATE => self::FIELD_DATE, + MYSQLI_TYPE_TIMESTAMP => self::FIELD_DATETIME, + MYSQLI_TYPE_TIME => self::FIELD_DATETIME, + MYSQLI_TYPE_DATETIME => self::FIELD_DATETIME, + MYSQLI_TYPE_ENUM => self::FIELD_TEXT, // eventually self::FIELD_INTEGER + MYSQLI_TYPE_SET => self::FIELD_TEXT, // eventually self::FIELD_INTEGER + MYSQLI_TYPE_STRING => self::FIELD_TEXT, + MYSQLI_TYPE_VAR_STRING=> self::FIELD_TEXT, + MYSQLI_TYPE_TINY_BLOB => self::FIELD_BINARY, + MYSQLI_TYPE_MEDIUM_BLOB=> self::FIELD_BINARY, + MYSQLI_TYPE_LONG_BLOB => self::FIELD_BINARY, + MYSQLI_TYPE_BLOB => self::FIELD_BINARY, + ); + + $count = mysqli_num_fields($this->resource); + $this->meta = $this->convert = array(); + for ($index = 0; $index < $count; $index++) { + $info = (array) mysqli_fetch_field_direct($this->resource, $index); + $native = $info['native'] = $info['type']; + + if ($info['flags'] & MYSQLI_AUTO_INCREMENT_FLAG) // or 'primary_key' ? + $info['type'] = self::FIELD_COUNTER; + else { + $info['type'] = isset($types[$native]) ? $types[$native] : self::FIELD_UNKNOWN; +// if ($info['type'] == self::FIELD_TEXT && $info['length'] > 255) +// $info['type'] = self::FIELD_LONG_TEXT; + } + + $this->meta[$info['name']] = $info; + $this->convert[$info['name']] = $info['type']; + } + } + + +} // class DibiMySqliResult + + + + + +?> \ No newline at end of file diff --git a/dibi/drivers/odbc.php b/dibi/drivers/odbc.php new file mode 100644 index 00000000..cd9e4b31 --- /dev/null +++ b/dibi/drivers/odbc.php @@ -0,0 +1,273 @@ + + * @link http://texy.info/dibi/ + * @copyright Copyright (c) 2005-2006 David Grudl + * @license GNU GENERAL PUBLIC LICENSE + * @package dibi + * @category Database + * @version 0.5alpha (2006-05-26) for PHP5 + */ + + +// security - include dibi.php, not this file +if (!defined('dibi')) die(); + + +/** + * The dibi driver interacting with databases via ODBC connections + * + */ +class DibiOdbcDriver extends DibiDriver { + private + $conn; + + public + $formats = array( + 'NULL' => "NULL", + 'TRUE' => "-1", + 'FALSE' => "0", + 'date' => "#m/d/Y#", + 'datetime' => "#m/d/Y H:i:s#", + ); + + + + public static function connect($config) + { + if (!extension_loaded('odbc')) + return new DibiException("PHP extension 'odbc' is not loaded"); + + if (@$config['persistent']) + $conn = @odbc_pconnect($config['database'], $config['username'], $config['password']); + else + $conn = @odbc_connect($config['database'], $config['username'], $config['password']); + + if (!is_resource($conn)) + return new DibiException("Connecting error", array( + 'message' => odbc_errormsg(), + 'code' => odbc_error(), + )); + + $obj = new self($config); + $obj->conn = $conn; + return $obj; + } + + + + public function query($sql) + { + $res = @odbc_exec($this->conn, $sql); + + if (is_resource($res)) + return new DibiOdbcResult($res); + + if ($res === FALSE) + return new DibiException("Query error", $this->errorInfo($sql)); + + return TRUE; + } + + + public function affectedRows() + { + $rows = odbc_num_rows($this->conn); + return $rows < 0 ? FALSE : $rows; + } + + + public function insertId() + { + return FALSE; + } + + + public function begin() + { + return odbc_autocommit($this->conn, FALSE); + } + + + public function commit() + { + $ok = odbc_commit($this->conn); + odbc_autocommit($this->conn, TRUE); + return $ok; + } + + + public function rollback() + { + $ok = odbc_rollback($this->conn); + odbc_autocommit($this->conn, TRUE); + return $ok; + } + + + private function errorInfo($sql = NULL) + { + return array( + 'message' => odbc_errormsg($this->conn), + 'code' => odbc_error($this->conn), + 'sql' => $sql, + ); + } + + + + public function escape($value, $appendQuotes = FALSE) + { + $value = str_replace("'", "''", $value); + return $appendQuotes + ? "'" . $value . "'" + : $value; + } + + + public function quoteName($value) + { + return '[' . strtr($value, array('.' => '].[')) . ']'; + } + + + public function getMetaData() + { + trigger_error('Meta is not implemented yet.', E_USER_WARNING); + } + +} // class DibiOdbcDriver + + + + + + + +class DibiOdbcResult extends DibiResult +{ + private + $resource, + $meta, + $row = 0; + + + public function __construct($resource) + { + $this->resource = $resource; + } + + + public function rowCount() + { + // will return -1 with many drivers :-( + return odbc_num_rows($this->resource); + } + + + protected function doFetch() + { + return odbc_fetch_array($this->resource, $this->row++); + } + + + public function seek($row) + { + $this->row = $row; + } + + + protected function free() + { + odbc_free_result($this->resource); + } + + + public function getFields() + { + // cache + if ($this->meta === NULL) + $this->createMeta(); + + return array_keys($this->meta); + } + + + protected function detectTypes() + { + if ($this->meta === NULL) + $this->createMeta(); + } + + + /** this is experimental */ + public function getMetaData($field) + { + // cache + if ($this->meta === NULL) + $this->createMeta(); + + return isset($this->meta[$field]) ? $this->meta[$field] : FALSE; + } + + + /** this is experimental */ + private function createMeta() + { + // cache + if ($this->meta !== NULL) + return $this->meta; + + static $types = array( + 'CHAR' => self::FIELD_TEXT, + 'COUNTER' => self::FIELD_COUNTER, + 'VARCHAR' => self::FIELD_TEXT, + 'LONGCHAR' => self::FIELD_TEXT, + 'INTEGER' => self::FIELD_INTEGER, + 'DATETIME' => self::FIELD_DATETIME, + 'CURRENCY' => self::FIELD_FLOAT, + 'BIT' => self::FIELD_BOOL, + 'LONGBINARY'=> self::FIELD_BINARY, + 'SMALLINT' => self::FIELD_INTEGER, + 'BYTE' => self::FIELD_INTEGER, + 'BIGINT' => self::FIELD_INTEGER, + 'INT' => self::FIELD_INTEGER, + 'TINYINT' => self::FIELD_INTEGER, + 'REAL' => self::FIELD_FLOAT, + 'DOUBLE' => self::FIELD_FLOAT, + 'DECIMAL' => self::FIELD_FLOAT, + 'NUMERIC' => self::FIELD_FLOAT, + 'MONEY' => self::FIELD_FLOAT, + 'SMALLMONEY'=> self::FIELD_FLOAT, + 'FLOAT' => self::FIELD_FLOAT, + 'YESNO' => self::FIELD_BOOL, + // and many others? + ); + + $count = odbc_num_fields($this->resource); + $this->meta = $this->convert = array(); + for ($index = 1; $index <= $count; $index++) { + $native = strtoupper(odbc_field_type($this->resource, $index)); + $name = odbc_field_name($this->resource, $index); + $this->meta[$name] = array( + 'type' => isset($types[$native]) ? $types[$native] : self::FIELD_UNKNOWN, + 'native' => $native, + 'length' => odbc_field_len($this->resource, $index), + 'scale' => odbc_field_scale($this->resource, $index), + 'precision' => odbc_field_precision($this->resource, $index), + ); + $this->convert[$name] = $this->meta[$name]['type']; + } + } + + +} // class DibiOdbcResult + + +?> \ No newline at end of file diff --git a/dibi/drivers/postgresql.php b/dibi/drivers/postgresql.php new file mode 100644 index 00000000..e69de29b diff --git a/dibi/drivers/sqlite.php b/dibi/drivers/sqlite.php new file mode 100644 index 00000000..296552d6 --- /dev/null +++ b/dibi/drivers/sqlite.php @@ -0,0 +1,233 @@ + + * @link http://texy.info/dibi/ + * @copyright Copyright (c) 2005-2006 David Grudl + * @license GNU GENERAL PUBLIC LICENSE + * @package dibi + * @category Database + * @version 0.5alpha (2006-05-26) for PHP5 + */ + + +// security - include dibi.php, not this file +if (!defined('dibi')) die(); + + +/** + * The dibi driver for SQlite database + * + */ +class DibiSqliteDriver extends DibiDriver { + private + $conn; + + public + $formats = array( + 'NULL' => "NULL", + 'TRUE' => "1", + 'FALSE' => "0", + 'date' => "'Y-m-d'", + 'datetime' => "'Y-m-d H:i:s'", + ); + + + + public static function connect($config) + { + if (!extension_loaded('sqlite')) + return new DibiException("PHP extension 'sqlite' is not loaded"); + + if (empty($config['database'])) + return new DibiException("Database must be specified"); + + $errorMsg = ''; + if (empty($config['persistent'])) + $conn = @sqlite_open($config['database'], @$config['mode'], $errorMsg); + else + $conn = @sqlite_popen($config['database'], @$config['mode'], $errorMsg); + + if (!$conn) + return new DibiException("Connecting error", array( + 'message' => $errorMsg, + )); + + $obj = new self($config); + $obj->conn = $conn; + return $obj; + } + + + + public function query($sql) + { + $errorMsg = ''; + $res = @sqlite_query($this->conn, $sql, SQLITE_ASSOC, $errorMsg); + + if ($res === FALSE) + return new DibiException("Query error", array( + 'message' => $errorMsg, + 'sql' => $sql, + )); + + if (is_resource($res)) + return new DibiSqliteResult($res); + + return TRUE; + } + + + public function affectedRows() + { + $rows = sqlite_changes($this->conn); + return $rows < 0 ? FALSE : $rows; + } + + + public function insertId() + { + $id = sqlite_last_insert_rowid($this->conn); + return $id < 1 ? FALSE : $id; + } + + + public function begin() + { + return sqlite_query($this->conn, 'BEGIN'); + } + + + public function commit() + { + return sqlite_query($this->conn, 'COMMIT'); + } + + + public function rollback() + { + return sqlite_query($this->conn, 'ROLLBACK'); + } + + + public function escape($value, $appendQuotes = FALSE) + { + return $appendQuotes + ? "'" . sqlite_escape_string($value) . "'" + : sqlite_escape_string($value); + } + + + public function quoteName($value) + { + return $value; + } + + + + public function getMetaData() + { + trigger_error('Meta is not implemented yet.', E_USER_WARNING); + } + + + +} // class DibiSqliteDriver + + + + + + + + + +class DibiSqliteResult extends DibiResult +{ + private + $resource, + $meta; + + + public function __construct($resource) + { + $this->resource = $resource; + } + + + public function rowCount() + { + return sqlite_num_rows($this->resource); + } + + + protected function doFetch() + { + return sqlite_fetch_array($this->resource, SQLITE_ASSOC); + } + + + public function seek($row) + { + return sqlite_seek($this->resource, $row); + } + + + protected function free() + { + } + + + public function getFields() + { + // cache + if ($this->meta === NULL) + $this->createMeta(); + + return array_keys($this->meta); + } + + + protected function detectTypes() + { + if ($this->meta === NULL) + $this->createMeta(); + } + + + /** this is experimental */ + public function getMetaData($field) + { + // cache + if ($this->meta === NULL) + $this->createMeta(); + + return isset($this->meta[$field]) ? $this->meta[$field] : FALSE; + } + + + /** this is experimental */ + private function createMeta() + { + $count = sqlite_num_fields($this->resource); + $this->meta = $this->convert = array(); + for ($index = 0; $index < $count; $index++) { + $name = sqlite_field_name($this->resource, $index); + $this->meta[$name] = array('type' => self::FIELD_UNKNOWN); + $this->convert[$name] = self::FIELD_UNKNOWN; + } + } + + +} // class DibiSqliteResult + + + + + +?> \ No newline at end of file diff --git a/dibi/libs/date.type.demo.php b/dibi/libs/date.type.demo.php new file mode 100644 index 00000000..a7c400c1 --- /dev/null +++ b/dibi/libs/date.type.demo.php @@ -0,0 +1,101 @@ + + * @link http://texy.info/dibi/ + * @copyright Copyright (c) 2005-2006 David Grudl + * @license GNU GENERAL PUBLIC LICENSE + * @package dibi + * @category Database + * @version 0.5alpha (2006-05-26) for PHP5 + */ + + +// security - include dibi.php, not this file +if (!defined('dibi')) die(); + + +// required since PHP 5.1.0 +// todo: +if (function_exists('date_default_timezone_set')) + date_default_timezone_set('Europe/Prague'); // or 'GMT' + + + +/** + * Pseudotype for UNIX timestamp representation + */ +class TDate implements IDibiVariable +{ + /** + * Unix timestamp + * @var int + */ + protected $time; + + + + public function __construct($time = NULL) + { + if ($time === NULL) + $this->time = time(); // current time + + elseif (is_string($time)) + $this->time = strtotime($time); // try convert to timestamp + + else + $this->time = (int) $time; + } + + + + /** + * Format for SQL + * + * @param object destination DibiDriver + * @param string optional modifier + * @return string + */ + public function toSQL($driver, $modifier = NULL) + { + return date( + $driver->formats['date'], // format according to driver's spec. + $this->time + ); + } + + + + public function getTimeStamp() + { + return $this->time; + } + + +} + + + + +/** + * Pseudotype for datetime representation + */ +class TDateTime extends TDate +{ + + public function toSQL($driver, $modifier = NULL) + { + return date( + $driver->formats['datetime'], // format according to driver's spec. + $this->time + ); + } + +} + +?> \ No newline at end of file diff --git a/dibi/libs/driver.php b/dibi/libs/driver.php new file mode 100644 index 00000000..5e226e71 --- /dev/null +++ b/dibi/libs/driver.php @@ -0,0 +1,164 @@ + + * @link http://texy.info/dibi/ + * @copyright Copyright (c) 2005-2006 David Grudl + * @license GNU GENERAL PUBLIC LICENSE + * @package dibi + * @category Database + * @version 0.5alpha (2006-05-26) for PHP5 + */ + + +// security - include dibi.php, not this file +if (!defined('dibi')) die(); + + + +/** + * dibi Common Driver + * + */ +abstract class DibiDriver +{ + /** + * Current connection configuration + * @var array + */ + protected + $config; + + /** + * Describes how convert some datatypes to SQL command + * @var array + */ + public $formats = array( + 'NULL' => "NULL", // NULL + 'TRUE' => "1", // boolean true + 'FALSE' => "0", // boolean false + 'date' => "'Y-m-d'", // format used by date() + 'datetime' => "'Y-m-d H:i:s'", // format used by date() + ); + + + /** + * DibiDriver factory: creates object and connects to a database + * + * @param array connect configuration + * @return bool|object DibiDriver object on success, FALSE or Exception on failure + */ + abstract static public function connect($config); + + + + /** + * Driver initialization + * + * @param array connect configuration + */ + protected function __construct($config) + { + $this->config = $config; + } + + + /** + * Get the configuration descriptor used by connect() to connect to database. + * @see connect() + * @return array + */ + public function getConfig() + { + return $this->config; + } + + + + /** + * Executes the SQL query + * + * @param string SQL statement. + * @return object|bool Result set object or TRUE on success, Exception on failure + */ + abstract public function query($sql); + + + /** + * Gets the number of affected rows by the last INSERT, UPDATE or DELETE query + * + * @return int number of rows or FALSE on error + */ + abstract public function affectedRows(); + + + /** + * Retrieves the ID generated for an AUTO_INCREMENT column by the previous INSERT query + * @return int|bool int on success or FALSE on failure + */ + abstract public function insertId(); + + + /** + * Begins a transaction (if supported). + */ + abstract public function begin(); + + + /** + * Commits statements in a transaction. + */ + abstract public function commit(); + + + /** + * Rollback changes in a transaction. + */ + abstract public function rollback(); + + + + + /** + * Escapes the string + * @param string unescaped string + * @param bool quote string? + * @return string escaped and optionally quoted string + */ + abstract public function escape($value, $appendQuotes = FALSE); + + + /** + * Quotes SQL identifier (table's or column's name, etc.) + * @param string identifier + * @return string quoted identifier + */ + abstract public function quoteName($value); + + + + + /** + * Gets a information of the current database. + * + * @return DibiMetaData + */ + abstract public function getMetaData(); + + + + +} // class DibiDriver + + + + + + + +?> \ No newline at end of file diff --git a/dibi/libs/exception.php b/dibi/libs/exception.php new file mode 100644 index 00000000..ea59ced1 --- /dev/null +++ b/dibi/libs/exception.php @@ -0,0 +1,67 @@ + + * @link http://texy.info/dibi/ + * @copyright Copyright (c) 2005-2006 David Grudl + * @license GNU GENERAL PUBLIC LICENSE + * @package dibi + * @category Database + * @version 0.5alpha (2006-05-26) for PHP5 + */ + + +// security - include dibi.php, not this file +if (!defined('dibi')) die(); + + + +/** + * dibi exception class + * + */ +class DibiException extends Exception +{ + private + $info; + + + public function __construct($message, $info=NULL) { + + $this->info = $info; + + if (isset($info['message'])) + $message = "$message: $info[message]"; +/* + if (isset($info['sql'])) + $message .= "\n[SQL] $info[sql]"; +*/ + parent::__construct($message); + } + + + + public function getSql() + { + return @$this->info['sql']; + } + + +} // class DibiException + + + + + +function is_error($var) +{ + return ($var === FALSE) || ($var instanceof Exception); +} + + +?> \ No newline at end of file diff --git a/dibi/libs/parser.php b/dibi/libs/parser.php new file mode 100644 index 00000000..f2f14158 --- /dev/null +++ b/dibi/libs/parser.php @@ -0,0 +1,295 @@ + + * @link http://texy.info/dibi/ + * @copyright Copyright (c) 2005-2006 David Grudl + * @license GNU GENERAL PUBLIC LICENSE + * @package dibi + * @category Database + * @version 0.5alpha (2006-05-26) for PHP5 + */ + + +// security - include dibi.php, not this file +if (!defined('dibi')) die(); + + + +/** + * dibi parser + * + */ +class DibiParser +{ + private + $modifier, + $hasError, + $driver; + + + /** + * Generates SQL + * + * @param array + * @return string + */ + public function parse($driver, $args) + { + $sql = ''; + $this->driver = $driver; + $this->modifier = 0; + $this->hasError = false; + $command = null; + $lastString = null; + + foreach ($args as $index => $arg) { + $sql .= ' '; // always add simple space + + + // array processing (with or without modifier) + if (is_array($arg)) { + // determine type: set | values | list + if ($this->modifier) { + $type = $this->modifier; + $this->modifier = false; + } else { + // autodetect + if (is_int(key($arg))) + $type = 'L'; // LIST + else { + if (!$command) + $command = strtoupper(substr(ltrim($args[0]), 0, 6)); + + $type = $command == 'UPDATE' ? 'S' : 'V'; // SET | VALUES + } + } + + // build array + $vx = $kx = array(); + switch ($type) { + case 'S': // SET + foreach ($arg as $k => $v) + $vx[] = $this->driver->quoteName($k) . '=' . $this->formatValue($v); + + $sql .= implode(', ', $vx); + break; + + case 'V': // VALUES + foreach ($arg as $k => $v) { + $kx[] = $this->driver->quoteName($k); + $vx[] = $this->formatValue($v); + } + + $sql .= '(' . implode(', ', $kx) . ') VALUES (' . implode(', ', $vx) . ')'; + break; + + case 'L': // LIST + foreach ($arg as $k => $v) + $vx[] = $this->formatValue($v); + + $sql .= implode(', ', $vx); + break; + + case 'N': // NAMES + foreach ($arg as $v) + $vx[] = $this->driver->quoteName($v); + + $sql .= implode(', ', $vx); + break; + + default: + $this->hasError = true; + $sql .= "**Unknown modifier %$type**"; + } + + continue; + } + + + + // after-modifier procession + if ($this->modifier) { + if ($arg instanceof IDibiVariable) { + $sql .= $arg->toSql($this->driver, $this->modifier); + $this->modifier = false; + continue; + } + + if (!is_scalar($arg) && !is_null($arg)) { // array is already processed + $this->hasError = true; + $this->modifier = false; + $sql .= '**Unexpected '.gettype($arg).'**'; + continue; + } + + switch ($this->modifier) { + case "s": // string + $sql .= $this->driver->escape($arg, TRUE); + break; + case 'T': // date + $sql .= date($this->driver->formats['date'], is_string($arg) ? strtotime($arg) : $arg); + break; + case 't': // datetime + $sql .= date($this->driver->formats['datetime'], is_string($arg) ? strtotime($arg) : $arg); + break; + case 'b': // boolean + $sql .= $arg ? $this->driver->formats['TRUE'] : $this->driver->formats['FALSE']; + break; + case 'i': + case 'u': // unsigned int + case 'd': // signed int + $sql .= (string) (int) $arg; + break; + case 'f': // float + $sql .= (string) (float) $arg; // something like -9E-005 is accepted by SQL + break; + case 'n': // identifier name + $sql .= $this->driver->quoteName($arg); + break; + default: + $this->hasError = true; + $sql .= "**Unknown modifier %$this->modifier**"; + } + + $this->modifier = false; + continue; + } + + + // simple string means SQL + if (is_string($arg)) { + // double string warning + // (problematic with dibi::queryStart & dibi::queryAdd +// if ($lastString === $index-1) +// trigger_error("Is seems there is error in SQL near '$arg'.", E_USER_WARNING); + + $lastString = $index; + + // speed-up - is regexp required? + $toSkip = strcspn($arg, '`[\'"%'); + + if ($toSkip == strlen($arg)) { + $sql .= $arg; + } else { + $sql .= substr($arg, 0, $toSkip) + . preg_replace_callback('/ + (?=`|\[|\'|"|%) ## speed-up + (?: + `(.+?)`| ## 1) `identifier` + \[(.+?)\]| ## 2) [identifier] + (\')((?:\'\'|[^\'])*)\'| ## 3,4) string + (")((?:""|[^"])*)"| ## 5,6) "string" + %([a-zA-Z])$| ## 7) right modifier + (\'|") ## 8) lone-quote + )/xs', + array($this, 'callback'), + substr($arg, $toSkip) + ); + } + + continue; + } + + + // default processing + $sql .= $this->formatValue($arg); + + } // for + + + if ($this->hasError) + return new DibiException('Errors during generating SQL', array('sql' => $sql)); + + return trim($sql); + } + + + + + + + private function formatValue($value) + { + if (is_string($value)) + return $this->driver->escape($value, TRUE); + + if (is_int($value) || is_float($value)) + return (string) $value; // something like -9E-005 is accepted by SQL + + if (is_bool($value)) + return $value ? $this->driver->formats['TRUE'] : $this->driver->formats['FALSE']; + + if (is_null($value)) + return $this->driver->formats['NULL']; + + if ($value instanceof IDibiVariable) + return $value->toSql($this->driver); + + $this->hasError = true; + return '**Unsupported type '.gettype($value).'**'; + } + + + + + + /** + * PREG callback for @see self::translate() + * @param array + * @return string + */ + private function callback($matches) + { + // [1] => `ident` + // [2] => [ident] + // [3] => ' + // [4] => string + // [5] => " + // [6] => string + // [7] => right modifier + // [8] => lone-quote + + if ($matches[1]) // SQL identifiers: `ident` + return $this->driver->quoteName($matches[1]); + + if ($matches[2]) // SQL identifiers: [ident] + return $this->driver->quoteName($matches[2]); + + if ($matches[3]) // SQL strings: '....' + return $this->driver->escape( strtr($matches[4], array("''" => "'")), true); + + if ($matches[5]) // SQL strings: "..." + return $this->driver->escape( strtr($matches[6], array('""' => '"')), true); + + if ($matches[7]) { // modifier + $this->modifier = $matches[7]; + return ''; + } + + if ($matches[8]) { // string quote + return '**Alone quote**'; + $this->hasError = true; + } + + die('this should be never executed'); + } + + + + + +} // class DibiParser + + + + + + + +?> \ No newline at end of file diff --git a/dibi/libs/resultset.php b/dibi/libs/resultset.php new file mode 100644 index 00000000..3df5bb1c --- /dev/null +++ b/dibi/libs/resultset.php @@ -0,0 +1,396 @@ + + * @link http://texy.info/dibi/ + * @copyright Copyright (c) 2005-2006 David Grudl + * @license GNU GENERAL PUBLIC LICENSE + * @package dibi + * @category Database + * @version 0.5alpha (2006-05-26) for PHP5 + */ + + +// security - include dibi.php, not this file +if (!defined('dibi')) die(); + + + +// PHP < 5.1 compatibility +if (!interface_exists('Countable', false)) { + interface Countable + { + function count(); + } +} + + + +/** + * dibi result-set abstract class + * + * + * $result = dibi::query('SELECT * FROM [table]'); + * $value = $result->fetchSingle(); + * $all = $result->fetchAll(); + * $assoc = $result->fetchAll('id'); + * $assoc = $result->fetchAll('active', 'id'); + * unset($result); + * + */ +abstract class DibiResult implements IteratorAggregate, Countable +{ + /** + * Column type in relation to PHP native type + */ + const + FIELD_TEXT = 's', // as 'string' + FIELD_BINARY = 'b', + FIELD_BOOL = 'l', // as 'logical' + FIELD_INTEGER = 'i', + FIELD_FLOAT = 'f', + FIELD_DATE = 'd', + FIELD_DATETIME = 't', + FIELD_UNKNOWN = '?', + + // special + FIELD_COUNTER = 'c'; // counter or autoincrement, is integer + + + /** + * Describes columns types + * @var array + */ + protected $convert; + + + + + /** + * Moves cursor position without fetching row + * @param int the 0-based cursor pos to seek to + * @return boolean TRUE on success, FALSE if unable to seek to specified record + */ + abstract public function seek($row); + + /** + * Returns the number of rows in a result set + * @return int + */ + abstract public function rowCount(); + + /** + * Gets an array of field names + * @return array + */ + abstract public function getFields(); + + /** + * Gets an array of meta informations about column + * @param string column name + * @return array + */ + abstract public function getMetaData($field); + + /** + * Acquires .... + * @return void + */ + abstract protected function detectTypes(); + + /** + * Frees the resources allocated for this result set + * @return void + */ + abstract protected function free(); + + /** + * Fetches the row at current position and moves the internal cursor to the next position + * internal usage only + * @return array|FALSE array() on success, FALSE if no next record + */ + abstract protected function doFetch(); + + + /** + * Fetches the row at current position, process optional type conversion + * and moves the internal cursor to the next position + * @return array|FALSE array() on success, FALSE if no next record + */ + final public function fetch() + { + $rec = $this->doFetch(); + if (!is_array($rec)) + return FALSE; + + // types-converting? + if ($t = $this->convert) { // little speed-up + foreach ($rec as $key => $value) { + if (isset($t[$key])) + $rec[$key] = $this->convert($value, $t[$key]); + } + } + + return $rec; + } + + + + /** + * Like fetch(), but returns only first field + * @return mixed value on success, FALSE if no next record + */ + final function fetchSingle() + { + $rec = $this->doFetch(); + if (!is_array($rec)) + return FALSE; + + // types-converting? + if ($t = $this->convert) { // little speed-up + $value = reset($rec); + $key = key($rec); + return isset($t[$key]) + ? $this->convert($value, $t[$key]) + : $value; + } + + return reset($rec); + } + + + + /** + * Fetches all records from table. Records , but returns only first field + * @param string associative colum [, param, ... ] + * @return array + */ + final function fetchAll() + { + @$this->seek(0); + $rec = $this->fetch(); + if (!$rec) + return array(); // empty resultset + + $assocBy = func_get_args(); + $arr = array(); + + if (!$assocBy) { // no associative array + $value = count($rec) == 1 ? key($rec) : NULL; + do { + $arr[] = $value === NULL ? $rec : $rec[$value]; + } while ($rec = $this->fetch()); + + return $arr; + } + + do { // make associative arrays + foreach ($assocBy as $n => $assoc) { + $val[$n] = $rec[$assoc]; + unset($rec[$assoc]); + } + + foreach ($assocBy as $n => $assoc) { + if ($n == 0) + $tmp = &$arr[ $val[$n] ]; + else + $tmp = &$tmp[$assoc][ $val[$n] ]; + + if ($tmp === NULL) + $tmp = $rec; + } + } while ($rec = $this->fetch()); + + return $arr; + } + + + + /** + * Fetches all records from table like $key => $value pairs + * @return array + */ + final function fetchPairs($key, $value) + { + @$this->seek(0); + $rec = $this->fetch(); + if (!$rec) + return array(); // empty resultset + + $arr = array(); + do { + $arr[ $rec[$key] ] = $rec[$value]; + } while ($rec = $this->fetch()); + + return $arr; + } + + + + /** + * Automatically frees the resources allocated for this result set + * @return void + */ + public function __destruct() + { + @$this->free(); + } + + + + public function setType($field, $type = NULL) + { + if ($field === TRUE) + $this->detectTypes(); + + elseif (is_array($field)) + $this->convert = $field; + + else + $this->convert[$field] = $type; + } + + + /** is this needed? */ + public function getType($field) + { + return isset($this->convert[$field]) ? $this->convert[$field] : NULL; + } + + + + public function convert($value, $type) + { + if ($value === NULL || $value === FALSE) + return $value; + + static $conv = array( + self::FIELD_TEXT => 'string', + self::FIELD_BINARY => 'string', + self::FIELD_BOOL => 'bool', + self::FIELD_INTEGER => 'int', + self::FIELD_FLOAT => 'float', + self::FIELD_COUNTER => 'int', + ); + + if (isset($conv[$type])) { + settype($value, $conv[$type]); + return $value; + } + + if ($type == self::FIELD_DATE) + return new TDate($value); // !!! experimental + + if ($type == self::FIELD_DATETIME) + return new TDateTime($value); // !!! experimental + + return $value; + } + + + /** these are the required IteratorAggregate functions */ + public function getIterator($offset = NULL, $count = NULL) + { + return new DibiResultIterator($this, $offset, $count); + } + /** end required IteratorAggregate functions */ + + + /** these are the required Countable functions */ + public function count() + { + return $this->rowCount(); + } + /** end required Countable functions */ + +} // class DibiResult + + + + + + + + +/** + * Basic Result set iterator. + * + * This can be returned by DibiResult::getIterator() method or directly using foreach: + * + * $result = dibi::query('SELECT * FROM table'); + * foreach ($result as $fields) { + * print_r($fields); + * } + * unset($result); + * + * + * Optionally you can specify offset and limit: + * + * foreach ($result->getIterator(2, 3) as $fields) { + * print_r($fields); + * } + * + */ +class DibiResultIterator implements Iterator +{ + private + $result, + $offset, + $count, + $record, + $row; + + + public function __construct(DibiResult $result, $offset = NULL, $count = NULL) + { + $this->result = $result; + $this->offset = (int) $offset; + $this->count = $count === NULL ? PHP_INT_MAX : (int) $count; + } + + + /** these are the required Iterator functions */ + public function rewind() + { + $this->row = 0; + @$this->result->seek($this->offset); + $this->record = $this->result->fetch(); + } + + + public function key() + { + return $this->row; + } + + + public function current() + { + return $this->record; + } + + + public function next() + { + $this->record = $this->result->fetch(); + $this->row++; + } + + + public function valid() + { + return is_array($this->record) && ($this->row < $this->count); + } + /** end required Iterator functions */ + + +} // class DibiResultIterator + + + +?> \ No newline at end of file diff --git a/examples/connect.php b/examples/connect.php new file mode 100644 index 00000000..c5b9d22b --- /dev/null +++ b/examples/connect.php @@ -0,0 +1,40 @@ + 'mysql', + 'host' => 'localhost', + 'username' => 'root', + 'password' => '***', + 'database' => 'test', + 'charset' => 'utf8', +), 1); + +if ($state instanceof Exception) { + echo $state; +} + +if (!dibi::isConnected()) { + die(); +} + + + +// second connection to odbc +dibi::connect(array( + 'driver' => 'odbc', + 'username' => 'root', + 'password' => '***', + 'database' => 'Driver={Microsoft Access Driver (*.mdb)};Dbq=C:\\Database.mdb', +), 3); + + +echo dibi::isConnected(); + + + +?> \ No newline at end of file diff --git a/examples/fetch.php b/examples/fetch.php new file mode 100644 index 00000000..e3074b35 --- /dev/null +++ b/examples/fetch.php @@ -0,0 +1,52 @@ +
+ 'mysqli',
+    'host'     => 'localhost',
+    'username' => 'root',
+    'password' => '***',
+    'database' => 'test',
+    'charset'  => 'utf8',
+));
+
+
+if (!dibi::isConnected())
+    die('Not connected');
+
+
+$res = dibi::query('SELECT * FROM table');
+
+// fetch a single value
+$value = $res->fetchSingle();
+
+// fetch complete result set
+$all = $res->fetchAll();
+
+// fetch complete result set like association array
+$assoc = $res->fetchAll('id');
+
+$assoc = $res->fetchAll('id', 'id2');
+
+// fetch complete result set like pairs key => value
+$pairs = $res->fetchPairs('id', 'name');
+
+
+// fetch row by row
+foreach ($res as $row => $fields) {
+    print_r($fields);
+}
+
+// fetch row by row with defined offset and limit
+foreach ($res->getIterator(2, 3) as $row => $fields) {
+    print_r($fields);
+}
+
+
+
+?>
\ No newline at end of file
diff --git a/examples/log.sql b/examples/log.sql
new file mode 100644
index 00000000..ba7c7fb6
--- /dev/null
+++ b/examples/log.sql
@@ -0,0 +1,14 @@
+Successfully connected to DB 'mysql'
+
+SELECT * FROM `nucleus_item` WHERE `inumber` =  38;
+-- Result: object(DibiMySqlResult) rows: 1
+-- Takes: 4.994 ms
+
+SELECT * FROM `nucleus_item` WHERE `inumber` <  38;
+-- Result: object(DibiMySqlResult) rows: 29
+-- Takes: 135.842 ms
+
+SELECT * FROM `*nucleus_item` WHERE `inumber` <  38;
+-- Result: Query error: Can't find file: '.\dgx\*nucleus_item.frm' (errno: 22)
+-- Takes: 121.454 ms
+
diff --git a/examples/logging.php b/examples/logging.php
new file mode 100644
index 00000000..702f3a30
--- /dev/null
+++ b/examples/logging.php
@@ -0,0 +1,31 @@
+
+ 'mysql',
+    'host'     => 'localhost',
+    'username' => 'root',
+    'password' => '***',
+    'database' => 'test',
+    'charset'  => 'utf8',
+));
+
+
+
+$res = dibi::query('SELECT * FROM [nucleus_item] WHERE [inumber] = %i', 38);
+
+
+$res = dibi::query('SELECT * FROM [nucleus_item] WHERE [inumber] < %i', 38);
+
+
+$res = dibi::query('SELECT * FROM [*nucleus_item] WHERE [inumber] < %i', 38);
+
+
+?>
\ No newline at end of file
diff --git a/examples/metatypes.php b/examples/metatypes.php
new file mode 100644
index 00000000..8d95dc2f
--- /dev/null
+++ b/examples/metatypes.php
@@ -0,0 +1,34 @@
+
+ 'mysql',
+    'host'     => 'localhost',
+    'username' => 'root',
+    'password' => '***',
+    'database' => 'test',
+    'charset'  => 'utf8',
+));
+
+$res = dibi::query('SELECT * FROM [nucleus_item] WHERE [inumber] <> %i', 38);
+
+
+$res = dibi::query('SELECT * FROM [nucleus_item] WHERE [inumber] <> %i', 38);
+
+// auto-convert this field to integer
+$res->setType('inumber', DibiResult::FIELD_INTEGER);
+$record = $res->fetch();
+var_dump($record);
+
+
+// auto-detect all types
+$res->setType(TRUE);
+$record = $res->fetch();
+var_dump($record);
+
+
+?>
diff --git a/examples/sql-builder.php b/examples/sql-builder.php
new file mode 100644
index 00000000..2a37d1ca
--- /dev/null
+++ b/examples/sql-builder.php
@@ -0,0 +1,48 @@
+
+ 'mysqli',
+    'host'     => 'localhost',
+    'username' => 'root',
+    'password' => '***',
+    'database' => 'test',
+    'charset'  => 'utf8',
+));
+
+
+$arr1 = array(1, 2, 3);
+$arr2 = array('one', 'two', 'three');
+$arr3 = array(
+    'a' => 'one',
+    'b' => 'two',
+    'c' => 'three',
+);
+$arr4 = array(
+    'A' => 12,
+    'B' => NULL,
+    'C' => new TDateTime(31542),
+    'D' => 'string',
+);
+
+dibi::test(
+"
+SELECT *
+FROM [test]
+WHERE ([test.a] LIKE %T", '1995-03-01', "
+  OR [b1] IN (", $arr1, ")
+  OR [b2] IN (", $arr2, ")
+  OR [b3] IN (%N", $arr3, ")
+  OR [b4] IN %V", $arr4, "
+  AND [c] = 'embedded '' string'
+  OR [d]=%d", 10.3, "
+  OR [true]=", true, "
+  OR [false]=", false, "
+  OR [null]=", NULL, "
+LIMIT 10");
+
+?>
diff --git a/license/license.gpl.cz.txt b/license/license.gpl.cz.txt
new file mode 100644
index 00000000..62607745
--- /dev/null
+++ b/license/license.gpl.cz.txt
@@ -0,0 +1,271 @@
+----------------------------------------------------------------------------------
+  Tento text je neoficiálním překladem GNU General Public License (GNU GPL). Nebyl
+vydán nadací Free Software Foundation a nevyjadřuje právní podstatu podmínek pro
+šíření softwaru používajícího GNU GPL - tomuto účelu slouží výhradně původní
+anglická verze GNU GPL. Přesto doufáme, že tento překlad pomůže českým čtenářům
+lépe porozumět licenci GNU GPL.
+----------------------------------------------------------------------------------
+
+                    GNU GENERAL PUBLIC LICENSE
+                 Český překlad verze 2, červen 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+                          675 Mass Ave, Cambridge, MA 02139, USA
+
+ Kopírování a distribuce doslovných kopií tohoto licenčního dokumentu jsou
+ dovoleny komukoliv, jeho změny jsou však zakázány.
+
+                            Preambule
+
+  Softwarové licence jsou většinou navrženy tak, že vám odebírají právo svobodného
+sdílení a úprav programů. Smyslem GNU General Public License je naproti tomu
+zaručit svobodu ke sdílení a úpravám svobodného softwaru - pro zajištění
+svobodného přístupu k tomuto softwaru pro všechny jeho uživatele. Tato General
+Public License se vztahuje na většinu softwaru nadace Free Software Foundation a
+na jakýkoli jiný program, jehož autor se přikloní k jejímu používání. (Některý
+další software od Free Software Foundation je namísto toho pokryt GNU Lesser
+General Public License.) Můžete ji rovněž použít pro své programy.
+
+  Pokud mluvíme o svobodném softwaru, máme na mysli svobodu, nikoliv cenu. Naše
+General Public License je navržena pro zajištění toho, že můžete svobodně šířit
+kopie svobodného softwaru (a účtovat si poplatek za tuto službu, pokud chcete),
+že obdržíte zdrojový kód anebo jej můžete získat, pokud ho chcete, že můžete
+tento software modifikovat nebo jeho části použít v nových svobodných programech;
+a že víte, že tyto věci smíte dělat.
+
+  Abychom mohli vaše práva chránit, musíme vytvořit omezení, která zakáží komukoli
+vám tato práva odepírat nebo vás žádat, abyste se těchto práv vzdal. Tato
+omezení se promítají do jistých povinností, kterým musíte dostát, pokud šíříte
+kopie dotyčného softwaru anebo ho modifikujete.
+
+  Například, šíříte-li kopie takového programu, ať již zdarma nebo za poplatek,
+musíte poskytnout příjemcům všechna práva, která máte sám. Musíte zaručit, že
+příjemci rovněž dostanou anebo mohou získat zdrojový kód. A musíte jim ukázat
+tyto podmínky, aby znali svá práva.
+
+  Vaše práva chráníme ve dvou krocích: (1) autorizací softwaru a (2) nabídkou této
+licence, která vám dává právoplatné svolení ke kopírování, šíření a modifikaci
+softwaru.
+
+  Kvůli ochraně každého autora i nás samotných chceme zajistit, aby každý chápal
+skutečnost, že pro svobodný software neplatí žádné záruky. Je-li software někým
+jiným modifikován a poslán dále, chceme, aby příjemci věděli, že to, co mají,
+není originál, takže jakékoliv problémy vnesené jinými se neodrazí na reputaci
+původních autorů.
+
+  Konečně, každý svobodný program je neustále ohrožen softwarovými patenty.
+Přejeme si zamezit nebezpečí, že redistributoři svobodného programu obdrží
+samostatně patentová osvědčení a tím učiní program vázaným. Abychom tomu
+zamezili, deklarovali jsme, že každý patent musí být buď vydán s tím, že
+umožňuje každému svobodné užití, anebo nesmí být vydán vůbec.
+
+  Přesná ustanovení a podmínky pro kopírování, šíření a modifikaci jsou uvedeny
+dále.
+
+       USTANOVENÍ A PODMÍNKY PRO KOPÍROVÁNÍ, DISTRIBUCI A MODIFIKACI
+
+  0. Tato licence se vztahuje na kterýkoliv program či jiné dílo, které obsahuje
+zmínku, umístěnou v něm držitelem autorských práv, o tom, že dílo může být
+šířeno podle ustanovení GNU General Public License. V dalším textu znamená "program"
+každý takový program nebo dílo a "dílo založené na programu" znamená buď program
+samotný anebo každé jiné dílo z něj odvozené, které podléhá autorskému zákonu:
+tím se míní dílo obsahující program nebo jeho část, buď doslovně anebo s
+modifikacemi, popřípadě v překladu do jiného jazyka. (Nadále je překlad
+zahrnován bez omezení pod pojem "modifikace".) Každý uživatel licence je
+označován jako "vy".
+
+Jiné činnosti než kopírování, šíření a modifikace nejsou pokryty touto licencí;
+sahají mimo její rámec. Akt spuštění programu není omezen a výstup z programu je
+pokryt pouze tehdy, jestliže obsah výstupu tvoří dílo založené na programu (nezávisle
+na tom, zda bylo vytvořeno činností programu). Posouzení platnosti předchozí
+věty závisí na tom, co program dělá.
+
+  1. Smíte kopírovat a šířit doslovné kopie zdrojového kódu programu tak, jak jste
+jej obdržel a na libovolném médiu, za předpokladu, že na každé kopii viditelně a
+náležitě zveřejníte zmínku o autorských právech a absenci záruky; ponecháte
+nedotčené všechny zmínky vztahující se k této licenci a k absenci záruky; a dáte
+každému příjemci spolu s programem kopii této licence.
+
+Za fyzický akt přenesení kopie můžete žádat poplatek a podle vlastního uvážení
+můžete nabídnout za poplatek záruční ochranu.
+
+  2. Můžete modifikovat vaši kopii či kopie programu anebo kterékoliv jeho části,
+a tak vytvořit dílo založené na programu a kopírovat a rozšiřovat takové
+modifikace či dílo podle podmínek paragrafu 1 výše, za předpokladu, že splníte
+všechny tyto podmínky:
+
+    a) Modifikované soubory musíte opatřit zřetelnou zmínkou uvádějící, že jste
+    soubory změnil a datum každé změny.
+
+    b) Musíte umožnit, aby jakékoliv vámi publikované či rozšiřované dílo, které
+    obsahuje zcela nebo zčásti program nebo jakoukoli jeho část, popřípadě je z
+    programu nebo jeho části odvozeno, mohlo být jako celek bezplatně poskytnuto
+    každé třetí osobě v souladu s ustanoveními této licence.
+
+    c) Pokud modifikovaný program pracuje normálně tak, že čte interaktivně povely,
+    musíte zajistit, že při nejběžnějším způsobu jeho spuštění vytiskne nebo zobrazí
+    hlášení zahrnující příslušnou zmínku o autorském právu a uvede, že neexistuje
+    žádná záruka (nebo případně, že záruku poskytujete vy), a že uživatelé mohou za
+    těchto podmínek program redistribuovat, a musí uživateli sdělit, jakým způsobem
+    může nahlédnout do kopie této licence. (Výjimka: v případě, že sám program je
+    interaktivní, avšak žádné takové hlášení nevypisuje, nepožaduje se, aby vaše
+    dílo založené na programu takové hlášení vypisovalo.)
+
+Tyto požadavky se vztahují k modifikovanému dílu jako celku. Pokud lze
+identifikovat části takového díla, které zřejmě nejsou odvozeny z programu a
+mohou být samy o sobě rozumně považovány za nezávislá a samostatná díla, pak se
+tato licence a její ustanovení nevztahují na tyto části, jsou-li šířeny jako
+nezávislá díla. Avšak jakmile tytéž části rozšiřujete jako část celku, jímž je
+dílo založené na programu, musí být rozšiřování tohoto celku podřízeno
+ustanovením této licence tak, že povolení poskytnutá dalším uživatelům se
+rozšíří na celé dílo, tedy na všechny jeho části bez ohledu na to, kdo kterou
+část napsal.
+
+Smyslem tohoto paragrafu tedy není získání práv na dílo zcela napsané vámi ani
+popírání vašich práv vůči němu; skutečným smyslem je výkon práva na řízení
+distribuce odvozených nebo kolektivních děl založených na programu.
+
+Pouhé spojení jiného díla, jež není na programu založeno, s programem (anebo
+dílem založeným na programu) na paměťovém nebo distribučním médiu neuvazuje toto
+jiné dílo do působnosti této licence.
+
+  3. Můžete kopírovat a rozšiřovat program (nebo dílo na něm založené, viz
+paragraf 2) v objektové anebo spustitelné podobě podle ustanovení paragrafů 1 a
+2 výše, pokud splníte některou z následujících náležitostí:
+
+    a) Doprovodíte jej zdrojovým kódem ve strojově čitelné formě. Zdrojový kód musí
+    být rozšiřován podle ustanovení paragrafů 1 a 2 výše, a to na médiu běžně
+    používaném pro výměnu softwaru; nebo
+
+    b) Doprovodíte jej písemnou nabídkou s platností nejméně tři roky, podle níž
+    poskytnete jakékoli třetí straně, za poplatek nepřevyšující vaše výdaje
+    vynaložené na fyzickou výrobou zdrojové distribuce, kompletní strojově čitelnou
+    kopii odpovídajícího zdrojového kódu, jenž musí být šířen podle ustanovení
+    paragrafů 1 a 2 výše na médiu běžně používaném pro výměnu softwaru; nebo
+
+    c) Doprovodíte jej informacemi, které jste dostal ohledně nabídky na poskytnutí
+    zdrojového kódu. (Tato alternativa je povolena jen pro nekomerční šíření a jenom
+    tehdy, pokud jste obdržel program v objektovém nebo spustitelném tvaru spolu s
+    takovou nabídkou, v souladu s položkou b výše.)
+
+Zdrojový kód k dílu je nejvhodnější formou díla z hlediska jeho případných
+modifikací. Pro dílo ve spustitelném tvaru znamená úplný zdrojový kód veškerý
+zdrojový kód pro všechny moduly, které obsahuje, plus jakékoli další soubory pro
+definici rozhraní, plus dávkové soubory potřebné pro kompilaci a instalaci
+spustitelného programu. Zvláštní výjimkou jsou však ty softwarové komponenty,
+které jsou normálně šířeny (buď ve zdrojové nebo binární formě) s hlavními
+součástmi operačního systému, na němž spustitelný program běží (tj. s
+překladačem, jádrem apod.). Tyto komponenty nemusí být šířeny se zdrojovým kódem,
+pokud ovšem komponenta sama nedoprovází spustitelnou podobu díla.
+
+Je-li šíření objektového nebo spustitelného kódu činěno nabídkou přístupu ke
+kopírování z určitého místa, potom se za distribuci zdrojového kódu počítá i
+nabídnutí ekvivalentního přístupu ke kopírování zdrojového kódu ze stejného
+místa, byť přitom nejsou třetí strany nuceny ke zkopírování zdrojového kódu
+spolu s objektovým.
+
+  4. Nesmíte kopírovat, modifikovat, poskytovat sublicence anebo šířit program
+jiným způsobem než výslovně uvedeným v této licenci. Jakýkoli jiný pokus o
+kopírování, modifikování, poskytnutí sublicence anebo šíření programu je
+neplatný a automaticky ukončí vaše práva daná touto licencí. Strany, které od
+vás obdržely kopie anebo práva v souladu s touto licencí, však nemají své
+licence ukončeny, dokud se jim plně podřizují.
+
+  5. Není vaší poviností tuto licenci přijmout, protože jste ji nepodepsal. Nic
+jiného vám však nedává možnost kopírovat nebo šířit program nebo odvozená díla.
+V případě, že tuto licenci nepřijmete, jsou tyto činnosti zákonem zakázány. Tím
+pádem modifikací anebo šířením programu (anebo každého díla založeného na
+programu) vyjadřujete své podřízení se licenci a všem jejím ustanovením a
+podmínkám pro kopírovaní, modifikování a šíření programu a děl na něm založených.
+
+  6. Pokaždé, když redistribuujete program (nebo dílo založené na programu),
+získává příjemce od původního držitele licence právo kopírovat, modifikovat a
+šířit program v souladu s těmito ustanoveními a podmínkami. Nesmíte klást žádné
+další překážky výkonu zde zaručených příjemcových práv. Nejste odpovědný za
+vymáhání dodržování této licence třetími stranami.
+
+  7. Jsou-li vám z rozhodnutí soudu, obviněním z porušení patentu nebo z
+jakéhokoli jiného důvodu (nejen v souvislosti s patenty) uloženy takové podmínky
+(ať již příkazem soudu, smlouvou nebo jinak), které se vylučují s podmínkami
+této licence, nejste tím osvobozen od podmínek této licence. Pokud nemůžete
+šířit program tak, abyste vyhověl zároveň svým závazkům vyplývajícím z této
+licence a jiným platným závazkům, nesmíte jej v důsledku toho šířit vůbec. Pokud
+by například patentové osvědčení nepovolovalo bezplatnou redistribuci programu
+všemi, kdo vaším přičiněním získají přímo nebo nepřímo jeho kopie, pak by jediný
+možný způsob jak vyhovět zároveň patentovému osvědčení i této licenci spočíval v
+ukončení distribuce programu.
+
+Pokud by se za nějakých specifických okolností jevila některá část tohoto
+paragrafu jako neplatná nebo nevynutitelná, považuje se za směrodatnou rovnováha
+vyjádřená tímto paragrafem a paragraf jako celek se považuje za směrodatný za
+jiných okolností.
+
+Smyslem tohoto paragrafu není navádět vás k porušování patentů či jiných
+ustanovení vlastnického práva, anebo tato ustanovení zpochybňovat; jediným jeho
+smyslem je ochrana integrity systému šíření svobodného softwaru, který je
+podložen veřejnými licenčními předpisy. Mnozí lidé poskytli své příspěvky do
+širokého okruhu softwaru šířeného tímto systémem, spolehnuvše se na jeho
+důsledné uplatňování; záleží na autorovi/dárci, aby rozhodl, zda si přeje šířit
+software pomocí nějakého jiného systému a žádný uživatel licence nemůže takové
+rozhodnutí zpochybňovat.
+
+Smyslem tohoto paragrafu je zevrubně osvětlit to, co je považováno za důsledek
+plynoucí ze zbytku této licence.
+
+  8. Pokud je šíření či použití programu v některých zemích omezeno buď patenty
+anebo autorsky chráněnými rozhraními, může držitel původních autorských práv,
+který svěřuje program do působnosti této licence, přidat výslovné omezení pro
+geografické šíření, vylučující takové země, takže šíření je povoleno jen v těch
+zemích nebo mezi těmi zeměmi, které nejsou tímto způsobem vyloučeny. Tato
+licence zahrnuje v tomto případě takové omezení přesně tak, jako by bylo zapsáno
+v textu této licence.
+
+  9. Free Software Foundation může čas od času vydávat upravené nebo nové verze
+General Public License. Takové nové verze se budou svým duchem podobat současné
+verzi, v jednotlivostech se však mohou lišit s ohledem na nové problémy či zájmy.
+
+Každé verzi je přiděleno rozlišující číslo verze. Pokud program specifikuje
+číslo verze, která se na něj vztahuje, a "všechny následující verze", můžete se
+podle uvážení řídit ustanoveními a podmínkami buďto oné konkrétní verze anebo
+kterékoliv následující verze, kterou vydala Free Software Foundation. Jestliže
+program nespecifikuje číslo verze této licence, můžete si vybrat libovolnou
+verzi, kterou kdy Free Software Foundation vydala.
+
+  10. Pokud si přejete zahrnout části programu do jiných svobodných programů,
+jejichž distribuční podmínky jsou odlišné, zašlete autorovi žádost o povolení. V
+případě softwaru, k němuž vlastní autorská práva Free Software Foundation,
+napište Free Software Foundation; někdy činíme výjimky ze zde uvedených
+ustanovení. Naše rozhodnutí bude vedeno dvěma cíli: zachováním svobodné povahy
+všech odvozenin našeho svobodného softwaru a podporou sdílení a opětovného
+využití softwaru obecně.
+
+                           ZÁRUKA SE NEPOSKYTUJE
+
+  11. VZHLEDEM K BEZPLATNÉMU POSKYTNUTÍ LICENCE K PROGRAMU SE NA PROGRAM
+NEVZTAHUJE ŽÁDNÁ ZÁRUKA, A TO V MÍŘE POVOLENÉ PLATNÝM ZÁKONEM. POKUD NENÍ
+PÍSEMNĚ STANOVENO JINAK, POSKYTUJÍ DRŽITELÉ AUTORSKÝCH PRÁV POPŘÍPADĚ JINÉ
+STRANY PROGRAM "TAK, JAK JE", BEZ ZÁRUKY JAKÉHOKOLI DRUHU, AŤ VÝSLOVNÉ NEBO
+VYPLÝVAJÍCÍ, VČETNĚ, ALE NIKOLI JEN, ZÁRUK PRODEJNOSTI A VHODNOSTI PRO URČITÝ
+ÚČEL. POKUD JDE O KVALITU A VÝKONNOST PROGRAMU, LEŽÍ VEŠKERÉ RIZIKO NA VÁS.
+POKUD BY SE U PROGRAMU PROJEVILY ZÁVADY, PADAJÍ NÁKLADY ZA VŠECHNU POTŘEBNOU
+ÚDRŽBU, OPRAVU ČI NÁPRAVU NA VÁŠ VRUB.
+
+  12. V ŽÁDNÉM PŘÍPADĚ, S VÝJIMKOU TOHO, KDYŽ TO VYŽADUJE PLATNÝ ZÁKON, ANEBO KDYŽ
+TO BYLO PÍSEMNĚ ODSOUHLASENO, VÁM NEBUDE ŽÁDNÝ Z DRŽITELŮ AUTORSKÝCH PRÁV ANI
+ŽÁDNÁ JINÁ STRANA, KTERÁ SMÍ MODIFIKOVAT ČI ŠÍŘIT PROGRAM V SOULADU S
+PŘEDCHOZÍMI USTANOVENÍMI, ODPOVĚDNI ZA ŠKODY, VČETNĚ VŠECH OBECNÝCH, SPECIÁLNÍCH,
+NAHODILÝCH NEBO NÁSLEDNÝCH ŠKOD VYPLÝVAJÍCÍCH Z UŽÍVÁNÍ ANEBO NESCHOPNOSTI
+UŽÍVAT PROGRAMU (VČETNĚ, ALE NIKOLI JEN, ZTRÁTY NEBO ZKRESLENÍ DAT, NEBO
+TRVALÝCH ŠKOD ZPŮSOBENÝCH VÁM NEBO TŘETÍM STRANÁM, NEBO SELHÁNÍ FUNKCE PROGRAMU
+V SOUČINNOSTI S JINÝMI PROGRAMY), A TO I V PŘÍPADĚ, ŽE TAKOVÝ DRŽITEL AUTORSKÝCH
+PRÁV NEBO JINÁ STRANA BYLI UPOZORNĚNI NA MOŽNOST TAKOVÝCH ŠKOD.
+
+                         KONEC USTANOVENÍ A PODMÍNEK
+
+
+----------------------------------------------------------------------------------
+(c) 2004, Stránky o svobodném software (info@gnu.cz)
+Tento překlad je z velké části založen na překladu od Ladislava Lhotky.
+V případě doplňujících informací nebo oprav kontaktujte maintainera: kysela@gnu.cz
+Poslední úprava: 22. 12. 2004
+----------------------------------------------------------------------------------
diff --git a/license/license.gpl.txt b/license/license.gpl.txt
new file mode 100644
index 00000000..8df7762b
--- /dev/null
+++ b/license/license.gpl.txt
@@ -0,0 +1,279 @@
+		    GNU GENERAL PUBLIC LICENSE
+		       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+                          675 Mass Ave, Cambridge, MA 02139, USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+			    Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+		    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+			    NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+		     END OF TERMS AND CONDITIONS