381 lines
13 KiB
PHP
381 lines
13 KiB
PHP
<?php
|
||
/*
|
||
* Класс таблицы для обмена данными в двоичном виде (по HTTP в zip)
|
||
*/
|
||
|
||
//Альтернатива unpack
|
||
function getBin($t,$v)
|
||
{
|
||
$v=unpack($t, $v);
|
||
return $v[1];
|
||
}
|
||
|
||
class TCField //Класс описания типа и названия столбцов
|
||
{ //Идентификаторы типов данных
|
||
static $BD_UINT1 = 0; //1 байт без знаковый
|
||
static $BD_UINT2 = 1; //2 байта без знаковый
|
||
static $BD_UINT4 = 3; //4 байта без знаковый
|
||
static $BD_INT1 = 10; //1 байт со знаковый
|
||
static $BD_INT2 = 11; //2 байта со знаковый
|
||
static $BD_INT4 = 13; //4 байта со знаковый
|
||
static $BD_INT8 = 17; //8 байт со знаковый
|
||
static $BD_FLOAT4 = 20; //4 байта
|
||
static $BD_FLOAT8 = 22; //8 байт double
|
||
static $BD_UTF8_1 = 100; //100 - utf8_1 string 1й байт размер строки в байтах
|
||
static $BD_UTF8_2 = 101; //101 - utf8_2 string 1х 2 байта размер строки в байтах
|
||
static $BD_UTF8_4 = 102; //101 - utf8_4 string 1х 4 байта размер строки в байтах
|
||
static $BD_BLOB_2 = 141;
|
||
static $BD_BLOB_4 = 143; //Двоичные данные uint4 количество байт
|
||
|
||
public $name=''; //Название столбца
|
||
public $type=-1; //Тип данных
|
||
public $value=''; //Запакованые данные
|
||
|
||
function TCField($name, $type)
|
||
{
|
||
//Преобразования для типов из PostgreSQL и MySQL
|
||
if($type=='bool' || $type=='tinyint') { $type=TCField::$BD_UINT1; } else
|
||
if($type=='int4' || $type=='int' || $type=='bigint') { $type=TCField::$BD_INT4; } else //bigint немного неправильно потому что это 64 бита но для андрод приложения не сделал
|
||
if($type=='int8') { $type=TCField::$BD_INT8; } else
|
||
if($type=='float4' || $type=='float') { $type=TCField::$BD_FLOAT4; } else
|
||
if($type=='float8' || $type=='NUMBER') { $type=TCField::$BD_FLOAT8; } else
|
||
if($type=='varchar' || $type=='VARCHAR2') { $type=TCField::$BD_UTF8_2; } else
|
||
if($type=='text') { $type=TCField::$BD_UTF8_4; } else
|
||
if($type=='bytea' || $type=='longblob') { $type=TCField::$BD_BLOB_4; } else
|
||
if($type=='timestamptz') { $type=TCField::$BD_UTF8_1; } else
|
||
if($type=='timestamp') { $type=TCField::$BD_UTF8_1; } else
|
||
if($type=='date') { $type=TCField::$BD_UTF8_1; }
|
||
|
||
$this->name=$name;
|
||
$this->type=$type;
|
||
}
|
||
|
||
//Получить PDO тип
|
||
function getPDOType()
|
||
{
|
||
if($this->type==TCField::$BD_UINT1) return PDO::PARAM_BOOL;
|
||
if($this->type==TCField::$BD_INT4) return PDO::PARAM_INT;
|
||
if($this->type==TCField::$BD_INT8) return PDO::PARAM_INT;
|
||
if($this->type==TCField::$BD_FLOAT4) return PDO::PARAM_STR;
|
||
if($this->type==TCField::$BD_FLOAT8) return PDO::PARAM_STR;
|
||
if($this->type==TCField::$BD_UTF8_1) return PDO::PARAM_STR;
|
||
if($this->type==TCField::$BD_UTF8_2) return PDO::PARAM_STR;
|
||
if($this->type==TCField::$BD_UTF8_4) return PDO::PARAM_STR;
|
||
if($this->type==TCField::$BD_BLOB_4) return PDO::PARAM_LOB;
|
||
return PDO::PARAM_STR;
|
||
}
|
||
|
||
//Прочитать значение из файла в соответствии с типом
|
||
function ReadValue($fileHandle)
|
||
{
|
||
if($this->type==TCField::$BD_UINT1)
|
||
{ $this->value=fread($fileHandle, 1);
|
||
}else
|
||
if($this->type==TCField::$BD_UINT2)
|
||
{ $this->value=fread($fileHandle, 2);
|
||
}else
|
||
if($this->type==TCField::$BD_UINT4)
|
||
{ $this->value=fread($fileHandle, 4);
|
||
}else
|
||
if($this->type==TCField::$BD_INT1)
|
||
{ $this->value=fread($fileHandle, 1);
|
||
}else
|
||
if($this->type==TCField::$BD_INT2)
|
||
{ $this->value=fread($fileHandle, 2);
|
||
}else
|
||
if($this->type==TCField::$BD_INT4)
|
||
{ $this->value=fread($fileHandle, 4);
|
||
}else
|
||
if($this->type==TCField::$BD_INT8)
|
||
{ $this->value=fread($fileHandle, 8);
|
||
}else
|
||
if($this->type==TCField::$BD_FLOAT4)
|
||
{ $this->value=fread($fileHandle, 4);
|
||
}else
|
||
if($this->type==TCField::$BD_FLOAT8)
|
||
{ $this->value=fread($fileHandle, 8);
|
||
}else
|
||
if($this->type==TCField::$BD_UTF8_1)
|
||
{ $s=getBin('C',fread($fileHandle, 1));
|
||
if($s==0) $this->value='';
|
||
else $this->value=fread($fileHandle, $s);
|
||
}else
|
||
if($this->type==TCField::$BD_UTF8_2)
|
||
{ $s=getBin('S',fread($fileHandle, 2));
|
||
if($s==0) $this->value='';
|
||
else
|
||
{ //В цикле так как из зип потока читало порциями
|
||
$this->value='';
|
||
while($s>strlen($this->value))
|
||
$this->value.=fread($fileHandle, $s-strlen($this->value));
|
||
}
|
||
}else
|
||
if($this->type==TCField::$BD_UTF8_4)
|
||
{ $s=getBin('I',fread($fileHandle, 4));
|
||
if($s==0) $this->value='';
|
||
else
|
||
{ //В цикле так как из зип потока читало порциями
|
||
$this->value='';
|
||
while($s>strlen($this->value))
|
||
$this->value.=fread($fileHandle, $s-strlen($this->value));
|
||
}
|
||
}else
|
||
if($this->type==TCField::$BD_BLOB_4)
|
||
{ $s=getBin('I',fread($fileHandle, 4));
|
||
if($s==0) $this->value='';
|
||
else
|
||
{ //В цикле так как из зип потока читало порциями
|
||
$this->value='';
|
||
while($s>strlen($this->value))
|
||
$this->value.=fread($fileHandle, $s-strlen($this->value));
|
||
}
|
||
}
|
||
}
|
||
|
||
function pack($value)
|
||
{
|
||
if($value===NULL){ return NULL; }
|
||
|
||
if($this->type==TCField::$BD_UINT1)
|
||
{ return pack("C",$value);
|
||
}else
|
||
if($this->type==TCField::$BD_UINT2)
|
||
{ return pack("S",$value);
|
||
}else
|
||
if($this->type==TCField::$BD_UINT4)
|
||
{ return pack("I",$value);
|
||
}else
|
||
if($this->type==TCField::$BD_INT1)
|
||
{ return pack("c",$value);
|
||
}else
|
||
if($this->type==TCField::$BD_INT2)
|
||
{ return pack("s",$value);
|
||
}else
|
||
if($this->type==TCField::$BD_INT4)
|
||
{ return pack("i",$value);
|
||
}else
|
||
if($this->type==TCField::$BD_INT8)
|
||
{
|
||
/*$str='0000000000000000000000000000000000000000000000000000000000000000'.decbin($value); //TODO decbin c 64 битами не работает на 32 битном php только 32 unpack('H*', '01010101');
|
||
$str=substr($str,-64,64);
|
||
for($ii=0;$ii<8;$ii++)//побайтно записываем все 8 байт
|
||
{ $szRez=$szRez.pack("C",bindec(substr($str,-8,8)));
|
||
$str=substr($str,0,-8);
|
||
}*/
|
||
}else
|
||
if($this->type==TCField::$BD_FLOAT4)
|
||
{
|
||
$value=str_replace(',','.',$value); //Чтоб не зависело от настроек оракла
|
||
return pack("f",$value);
|
||
}else
|
||
if($this->type==TCField::$BD_FLOAT8)
|
||
{
|
||
$value=str_replace(',','.',$value); //Чтоб не зависело от настроек оракла
|
||
return pack("d",$value);
|
||
}else
|
||
if($this->type==TCField::$BD_UTF8_1)
|
||
{ //$str=iconv('WINDOWS-1251', 'UTF-8', $value);
|
||
return pack("C",strlen($value)).$value;
|
||
}else
|
||
if($this->type==TCField::$BD_UTF8_2)
|
||
{ //$str=iconv('WINDOWS-1251', 'UTF-8', $value);
|
||
return pack("S",strlen($value)).$value;
|
||
}else
|
||
if($this->type==TCField::$BD_UTF8_4)
|
||
{ //$str=iconv('WINDOWS-1251', 'UTF-8', $value);
|
||
return pack("I",strlen($value)).$value;
|
||
}else
|
||
if($this->type==TCField::$BD_BLOB_4)
|
||
{ return pack("I",strlen($value)).$value;
|
||
}
|
||
}
|
||
|
||
function setValue($value) //пакуем данные в соответствии с типом
|
||
{
|
||
$this->value=$this->pack($value);
|
||
}
|
||
//Распаковываем данные в соответствии с типом
|
||
function getValue()
|
||
{
|
||
if($this->value===NULL) return NULL;
|
||
|
||
if($this->type==TCField::$BD_UINT1)
|
||
{ return getBin ("C", $this->value);
|
||
}else
|
||
if($this->type==TCField::$BD_UINT2)
|
||
{ return getBin ("S", $this->value);
|
||
}else
|
||
if($this->type==TCField::$BD_UINT4)
|
||
{ return getBin ("I", $this->value);
|
||
}else
|
||
if($this->type==TCField::$BD_INT1)
|
||
{ return getBin ("c", $this->value);
|
||
}else
|
||
if($this->type==TCField::$BD_INT2)
|
||
{ return getBin ("s", $this->value);
|
||
}else
|
||
if($this->type==TCField::$BD_INT4)
|
||
{ return getBin ("i", $this->value);
|
||
}else
|
||
if($this->type==TCField::$BD_INT8)
|
||
{ //return getBin ("S", $this->value);
|
||
}else
|
||
if($this->type==TCField::$BD_FLOAT4)
|
||
{ return getBin ("f", $this->value);
|
||
}else
|
||
if($this->type==TCField::$BD_FLOAT8)
|
||
{ return getBin ("d", $this->value);
|
||
}else
|
||
if($this->type==TCField::$BD_UTF8_1 || $this->type==TCField::$BD_UTF8_2 || $this->type==TCField::$BD_UTF8_4)
|
||
{ return $this->value;
|
||
}else
|
||
if($this->type==TCField::$BD_BLOB_2 || $this->type==TCField::$BD_BLOB_4)
|
||
{ return $this->value;
|
||
}
|
||
return '';
|
||
}
|
||
}
|
||
|
||
class TCTable
|
||
{
|
||
public $id=0; //Идентификатор таблицы
|
||
public $name=''; //Название таблицы
|
||
public $fields = array(); //Массив полей
|
||
private $nc=0; //Байтов под NULL значения
|
||
private $m_NULL; //NULL значения
|
||
private $m_file;
|
||
|
||
/**
|
||
* Конструктор
|
||
* @param Строка $name Название таблицы
|
||
* @param Целое $id Идентификатор таблицы
|
||
*/
|
||
function TCTable($name,$id)
|
||
{ $this->name=$name;
|
||
$this->id=$id;
|
||
}
|
||
|
||
//Открыть таблицу по названию файла
|
||
function OpenTableF($file)
|
||
{
|
||
if(file_exists($file))
|
||
{
|
||
$this->OpenTableH(fopen($file,'r'));
|
||
}
|
||
}
|
||
|
||
//Открыть таблицу из HANDLE (файла)
|
||
function OpenTableH($handle)
|
||
{
|
||
$this->m_file=$handle;
|
||
|
||
if(getBin('S',fread($this->m_file, 2))!=65500) return false; //id файла
|
||
if(getBin('S',fread($this->m_file, 2))!=1) return false; //Версия файла
|
||
$this->id=getBin('I',fread($this->m_file, 4)); //ID таблицы или запроса (4 байта можно сделать 2)
|
||
if(getBin('C',fread($this->m_file, 1))!=0) return false; //Только плотные таблицы
|
||
$this->name=fread($this->m_file, getBin('C',fread($this->m_file, 1))); //Название таблицы
|
||
//Считываем столбцы
|
||
$count=getBin('C',fread($this->m_file, 1)); //Количество столбцов
|
||
for($i=0;$i<$count;$i++)
|
||
{
|
||
$field=new TCField (fread($this->m_file, getBin('C',fread($this->m_file, 1))),getBin('C',fread($this->m_file, 1)));
|
||
$this->addField($field);
|
||
//echo $field->name.'<br/>';
|
||
}
|
||
return true;
|
||
}
|
||
//Открыть таблицу из потока
|
||
//OpenTable
|
||
|
||
//Прочитать следующую запись из таблицы
|
||
function ReadNextRecord()
|
||
{
|
||
if(feof($this->m_file)) return; //Неработает
|
||
|
||
$this->m_NULL='';
|
||
for($j=0;$j<$this->nc;$j++) //Побайтно
|
||
{
|
||
$v=fread($this->m_file, 1);
|
||
if(strlen($v)==0) return; //Проверка конца файла
|
||
$v=getBin('C',$v);
|
||
$v=decbin($v);
|
||
for($i=strlen($v);$i<8;$i++) $v='0'.$v;
|
||
$this->m_NULL.=$v;
|
||
}
|
||
|
||
$this->clearRows();
|
||
for($i=0;$i<count($this->fields);$i++)
|
||
{
|
||
if($this->m_NULL[$i]=="1")
|
||
{
|
||
$this->fields[$i]->ReadValue($this->m_file);
|
||
}
|
||
}
|
||
return true;
|
||
}
|
||
|
||
//Добавить поле к таблице
|
||
function addField($field)
|
||
{ if($field!=NULL)
|
||
{ $this->fields[]=$field;
|
||
$this->nc=ceil(count($this->fields)/8.0); //Байтов под NULL
|
||
}
|
||
}
|
||
//Получить заголовок плотной таблицы в виде двоичной строки
|
||
function getHeader()
|
||
{
|
||
$szRez=''; //Данные из таблицы в двоичном формате
|
||
$szRez=$szRez.pack("S",65500); //id файла (2 байта)
|
||
$szRez=$szRez.pack("S",1); //Версия файла (2 байта)
|
||
$szRez=$szRez.pack("I",$this->id); //ID таблицы или запроса (4 байта)
|
||
$szRez=$szRez.pack("C",0); //Тип таблицы 0-"Плотная" 1-"Мягкая" (1 байт)
|
||
$szRez=$szRez.pack("C",strlen($this->name)).$this->name; //UTF8_1 строка
|
||
$szRez=$szRez.pack("C",count($this->fields)); //Колво столбцов (1 байт)
|
||
//Записываем id типов столбцов
|
||
for($i=0;$i<count($this->fields);$i++)
|
||
{
|
||
$szRez.=pack("C",strlen($this->fields[$i]->name)).$this->fields[$i]->name;
|
||
$szRez.=pack("C",$this->fields[$i]->type);
|
||
}
|
||
return $szRez;
|
||
}
|
||
//Получить данные 1 записи в виде строки
|
||
function getCol()
|
||
{ $szRez='';
|
||
//Запишем NULL значения побайтно (1-есть данные 0-нету данных)
|
||
$str='';
|
||
for($i=0;$i<$this->nc*8;$i++)
|
||
{
|
||
if($i>=count($this->fields)) $str.='0'; else
|
||
if($this->fields[$i]->value===NULL) $str.='0'; else $str.='1';
|
||
if(strlen($str)==8)
|
||
{ $szRez=$szRez.pack("C",bindec($str));
|
||
$str='';
|
||
}
|
||
}
|
||
//Запишем сами данные в строку
|
||
for($i=0;$i<count($this->fields);$i++)
|
||
{
|
||
$szRez.=$this->fields[$i]->value;
|
||
}
|
||
return $szRez;
|
||
}
|
||
|
||
//Row очистить запись
|
||
function clearRows()
|
||
{ for($i=0;$i<count($this->fields);$i++)
|
||
{ $this->fields[$i]->value=null;
|
||
}
|
||
}
|
||
//Получить обьект столбца по имени
|
||
function getRowByName($name)
|
||
{ for($i=0;$i<count($this->fields);$i++)
|
||
{ if($this->fields[$i]->name==$name) return $this->fields[$i];
|
||
}
|
||
}
|
||
//Получить объект столбца по номеру
|
||
function getRowByNum($num)
|
||
{ return $this->fields[$num];
|
||
}
|
||
}
|
||
?>
|