<?php

/*
	textdb.php
	copyright (c)2002-2015 econosys system
	http://flatsystems.net/econosys_system/

	メソッド一覧
	select()
	select_pager()
	count()
	insert()
	update()
	delete()
	delete_all()
	move_top()
	
	help()

*/

	// Version 
	// Version 0.070：helpメソッド追加
	// Version 0.071：データファイルの拡張子を（.cgi）以外に指定できるように
	// Version 0.072：YAMLをキャッシュして高速化
	// Version 0.080：select_one メソッドの追加
	// Version 0.081：update時のbugfix
	// Version 0.090：カラムが「csv」のときは自動的にカンマでセパレートして配列に。
	// Version 0.091：update時にidが空の場合errorとするように。
	// Version 0.092：find_or_create move_top メソッドの追加
	// Version 0.093：CSVカラムが空のときは配列を生成しないように
	// Version 0.094：実行時間計測
	// Version 0.095：update時の不具合修正
	// Version 0.096：sypcを必要なときだけ読み込むように
	// Version 0.097：カウントファイルがない場合に自動生成
	// Version 0.098：select時に複数カラムでand検索
	// Version 0.098：delete時にidの指定がなくてもエラーとしない
	// Version 0.099：カウントファイル自動生成時のbugfix
	// Version 0.1  ：細かいbugfix
	// Version 0.11 ：insert時のbottomオプション追加
	// Version 0.12 ：カウントファイル自動生成時のbugfixその2
	// Version 0.13 ：SELECT時に先頭に # がある行は無視
	// Version 0.14 ：LOCKの解除を fclose で行うことに。またselect時にはロックしないように。
	// Version 0.15 ：Perlメモ方式のファイルロックに変更。
	// Version 0.16 ：起動時にロックファイル存在を調べないように。以上ロック解除ロジックの修正
	// Version 0.17 ：select_pager_all 追加
	// Version 0.18 ：split 関数使用を中止
	// Version 0.19 ：データにコメントを持てるよう仕様変更



class textdb
{
	var $version          = '0.19';
	var $notice           = '本プログラムは econosys system の著作物です。著作者の許可無き改変、別システムでの使用を禁止します。copyright (c) econosys system http://flatsystems.net/econosys_system/';
	var $table_name       = '';
	var $ext              = '';
	var $columns          = array();
	var $types            = array();
	var $comments         = array();
	var $primary          = array();
	var $data_dir         = '';
	var $lfh;

	var $dump_encoding_to = null;
	var $_config          = array();
	var $dump             = 0;

	//========================================== コンストラクタ(DB)
	function textdb($yml_filename, $table_name='', $data_dir='', $ext='cgi' ){

		umask(0);

		$yml_cache_filename = $yml_filename.'.cache';

		if ( strcmp($table_name,'')==0 ){ die('textdb:error: please set table_name.'); }
		if ( strcmp($data_dir,'')==0 ){ die('textdb:error: please set data_dir.'); }
		$this->table_name = $table_name;
		$this->ext        = $ext;

		// data_dir lockfile
		$data_dir = preg_replace('/\/$/', '', $data_dir);
		$this->_check_data_dir($data_dir);
		$this->data_dir = $data_dir;

		// load YAML
		$cached_flag = 0;
        if ( file_exists($yml_filename) && file_exists($yml_cache_filename) ){
			if ( @filemtime($yml_filename) < @filemtime($yml_cache_filename)  ){
				$cached_flag = 1;
			}
		}
		
		// cache がある場合
		if ($cached_flag == 1){
	        $this->_config = unserialize(file_get_contents($yml_cache_filename));		
		}
		// cache ない場合
		else{
			require_once('spyc/spyc.php');
			$this->_config = Spyc::YAMLLoad($yml_filename);
			$f = fopen($yml_cache_filename, 'wb');
			fwrite($f, serialize($this->_config));
			fclose($f);
		}
		if ( ! isset($this->_config[$table_name]['desc']) ){ die("textdb:error: table_name [{$table_name}] is not defined in YAML."); }


		// columns , comments
		foreach ( $this->_config[$table_name]['desc'] as $k => $v ){
			array_push($this->columns,$v[0]);

			$t = '';
			if ( isset($v[1]) ){
				$t = $v[1];
			}
			array_push($this->types,$t);

			$c = '    ';
			if ( isset($v[2]) ){
				$c = $v[2];
			}
			array_push($this->comments,$c);
		}
		// primary
		$this->primary[0] = $this->columns[0];

		// $this->dump($this);

	}


	//========================================== _encode_csv
	function _encode_csv($text){
		$text = preg_replace('/\</','&lt;',$text);
		$text = preg_replace('/\>/','&gt;',$text);

		$text = preg_replace("/\r\n/", "\n", $text);
		$text = preg_replace("/\r/", "\n", $text);
		$text = preg_replace("/\n/", "<br />", $text);

		return $text;
	}


	//========================================== _decode_csv
	function _decode_csv( $ar=array() ){
		$new_ar = array();
		foreach ($ar as $k => $v){
			$v = preg_replace( "/\<br \/\>/", "\n", $v);
			$v = preg_replace('/&lt;/', '<' ,$v);
			$v = preg_replace('/&gt;/', '>' ,$v);
			$new_ar[$k] = $v;
			// カラムのタイプが「csv」の場合は配列を作る
			if( strcmp($this->_check_column_data_type($k),'csv')==0 ){
				if (strcmp($v,'')!=0){
					$new_ar[$k.'_loop'] = preg_split('{,}',$v);
				}
			}
		}
//$this->dump($new_ar);
		return $new_ar;
	}


	//========================================== _check_column_data_type
	function _check_column_data_type( $column_name='' ){
		foreach ( $this->_config[$this->table_name]['desc'] as $k => $v){
			if( strcmp($v[0],$column_name)==0 ){
				return $v[1];
			}
		}
	}


	//========================================== _check_data_dir
	function _check_data_dir($data_dir){
		// dir
		if (! is_dir($data_dir) ){ die("textdb:error: data_dir [{$data_dir}] is not exists."); }
//OFF		if (! is_file("{$data_dir}/lockfile") ){die("textdb:error: lock_file [{$data_dir}/lockfile] is not exists."); }
		//OFF if (! chmod("{$data_dir}/lockfile", 0666) ){ die("textdb:error: cannot chmod lock_file [{$data_dir}/lockfile] ."); }
		// file, tmpfile
		// OFF if (! touch("{$data_dir}/{$this->table_name}.{$this->ext}") ){ die("textdb:error: cannot create data_file [{$data_dir}/{$this->table_name}.{$this->ext}] ."); }

 	}



	//========================================== _lock
	function _lock($options = array()){
		$this->lfh = array_merge(array(
								 'dir'      => $this->data_dir,
								 'basename' => 'lockfile',
								 'timeout'  => 60,
								 'trytime'  => 10), $options);
		$this->lfh['dir'] = preg_replace('{/$}','',$this->lfh['dir']);
		$this->lfh['path'] = $this->lfh['dir'] .'/'. $this->lfh['basename'];
	
		for ($i = 0; $i < $this->lfh['trytime']; $i++, sleep(1)) {
			if ( @rename($this->lfh['path'], $this->lfh['current'] = $this->lfh['path'] . time()) ) {
				return $this->lfh;
			}
		}
		$filelist = $this->_my_scandir($this->lfh['dir']);
		foreach ($filelist as $file) {
			if (preg_match('/^' . $this->lfh['basename'] . '(\d+)/', $file, $matches)) {
//$this->dump($matches);
//$a = time() - $matches[1];
//$this->dump($a);
//$this->dump($this->lfh['timeout']);
				if (time() - $matches[1] > $this->lfh['timeout']
				  and rename($this->lfh['dir'] .'/'. $matches[0],
							 $this->lfh['current'] = $this->lfh['path'] . time())
				) {
					return $this->lfh;
				}
				break;
			}
		}
		return false;
	}


	//========================================== _unlock
	function _unlock() {
		@rename($this->lfh['current'], $this->lfh['path']);
	}


	//========================================== _my_scandir
	function _my_scandir($dir) {
		$dh  = opendir($dir);
		$files = array();
		while (false !== ($filename = readdir($dh))) {
			$files[] = $filename;
		}
		return $files;
	}



	//========================================== _count_increment
	function _count_increment(){

		$pid = getmypid();
		$tmp_filename = "{$this->data_dir}/{$this->table_name}.count.cgi".$pid.time();
		$filename     = "{$this->data_dir}/{$this->table_name}.count.cgi";

		// $fp =fopen($filename, 'r') or die("textdb:error cannot open count file");
		$fp    = null;
		$count = null;
		if ( is_file($filename) ){
			$fp = fopen($filename, 'r');
			$count = fgets($fp, 1024);
		}
		else{
		
			$tmp_all = $this->select(array(),0,99999);
			// idで並び替え
			$primary_column_name = $this->primary[0];
			$ids = array();
			foreach ($tmp_all as $value) {
				array_push($ids,$value[$primary_column_name]);
			}
			array_multisort($ids, SORT_DESC, $tmp_all);
//$this->dump($tmp_all);
			$tmp_hash = @$tmp_all[0];
//$this->dump($tmp_hash);
			$primary_column_name = $this->primary[0];
			if ( isset($tmp_hash[$primary_column_name]) ){
				$count = intval($tmp_hash[$primary_column_name]);
			}
			else{
				$count = 0;
			}
			$fp = fopen($filename, 'w');
			fwrite($fp, $count);
		}
		fclose($fp);
		$count++;

		$fptmp =fopen($tmp_filename, 'w');
		fwrite($fptmp, $count);
		fclose($fptmp);

		rename($tmp_filename, $filename);

		return $count;
	}



	//========================================== select
	function select( $hash=array(), $start=0, $limit=5 ){

		$time_start=0; $time_end=0; $time_work=0;

		if ($this->dump==1){
			// 時間の計測 start
			$time_start=$this->_getmicrotime();
		}

		// ORDER_BY
		$order_by = false;
		if ( isset($hash['ORDER_BY']) ){
			if ( $hash['ORDER_BY'] ){
				$order_by = $hash['ORDER_BY'];
				unset($hash['ORDER_BY']);
			}
		}

		$select_all_flag = false;
		if (count($hash)==0){
			$select_all_flag = true;
		}

		if ($this->dump==1){
			print "============================== <br>SELECT  start:{$start} limit:{$limit}\n";
			$this->dump($hash);
		}

		// check columns
		$this->_check_columns($hash);
//$this->dump($hash);

//OFF		// lock
//OFF		$this->_lock();

		// filename
		$filename     = "{$this->data_dir}/{$this->table_name}.{$this->ext}";

		// read_data(select)
		$data_i   = 0;
		$data_sum = 0;
		$debug_i  = 0;

		$read_data_loop = array();
		$primary_column_name = $this->primary[0];
		$fp =fopen($filename, 'r') or die("textdb:error cannot open count file ({$filename})");
		while (!feof($fp)) {
			$debug_i++;
			$line = fgets($fp, 9999);
			$h = $this->_read_data($line);
			// $this->dump($h);
			if ($h[$primary_column_name] == false){ continue; }	// skip null line.
			if ( preg_match('{^#}',$line)){ continue; }	// skip Comment line.

			$flag = false;

			if ( $select_all_flag ){
				$flag = true;
				$hash_total_no = 0;
				$ture_total_no = 0;
			}
			else{
				$hash_total_no = count($hash);
				$ture_total_no = 0;
//$this->dump($hash_total_no);
				foreach ($hash as $k => $v){
					//print "キーは（{$k}）値（{$v}）で検索<br>データファイルのキー（{$k}）の値は（{$h[$k]}） <br>\n";
					//$this->dump($h);
					if ( strcmp($h[$k],$hash[$k])==0 ){ $flag = true; $ture_total_no++; }
					// else{ $flag = false; }
				}
			}
			if ($flag && $hash_total_no==$ture_total_no){
				if ( $data_sum >= $limit){ break; }
				elseif ($data_i >= $start && $data_sum < $limit){
					// decode
					$h = $this->_decode_csv($h);
					array_push($read_data_loop, $h);
					$data_sum++;
				}
				$data_i++;
			}
		}

//$this->dump("{$data_sum}:{$data_i}:{$debug_i}");

//OFF		// unlock
//OFF		$this->_unlock();

		// ORDER_BY
		if ( $order_by && ( count($read_data_loop) > 0 )){
			// ※ カラムチェック
			// $this->dump($order_by);
			$column = false;
			$order  = false;
			if ( preg_match('/ /',$order_by) ){
				list($column, $order) = explode(' ', $order_by);
			}
			else{
				$column = $order_by;
			}
			// $this->dump($column);
			$hash = array( $column => 'dummy' );
			$this->_check_columns($hash);
			
			foreach ($read_data_loop as $k => $v) {
				$sort_key[$k] = $v[$column];
			}
			if ( preg_match('/DESC$/', $order_by) ){
				array_multisort( $sort_key, SORT_DESC, $read_data_loop );
			}
			else{
				array_multisort( $sort_key, SORT_ASC, $read_data_loop );
			}
			// $this->dump($read_data_loop);
		}

		if ($this->dump==1){
			$this->dump($read_data_loop);
			// 時間の計測 end
			$time_end=$this->_getmicrotime();
			$time_work=$time_end - $time_start;
			$this->dump("time:{$time_work}\n");
		}

		return $read_data_loop;
	}



	//========================================== select_pager
	function select_pager( $hash=array(), $results_per_page=20, $page_no=1 ){
		$start = $results_per_page * ($page_no - 1);
		return ($this->select($hash, $start, $results_per_page) );
	}



	//========================================== select_pager_all
	function select_pager_all( $hash=array(), $results_per_page=20 ){
		$max   = $this->count($hash);

		$out = array();
		for ($i=0; $i<($max/$results_per_page); $i++){
//print (($max/$results_per_page)+1);
			$page = $i+1;
			$tmp =  $this->select_pager($hash, $results_per_page, $page );
			array_push($out,$tmp);
		}
		
		return $out;
	}



	//========================================== select_one
	function select_one( $hash=array() ){
		$d = $this->dump;
		$this->dump=0;

		$time_start=0; $time_end=0; $time_work=0;
		if ($d==1){
			// 時間の計測 start
			$time_start=$this->_getmicrotime();
		}

		$r = $this->select($hash, 0, 1 );

		$this->dump=$d;

		if ($this->dump==1){
			print "============================== <br>SELECT_ONE\n";
			$this->dump($hash);
			$this->dump($r[0]);
			// 時間の計測 end
			$time_end=$this->_getmicrotime();
			$time_work=$time_end - $time_start;
			$this->dump("time:{$time_work}\n");
		}

		if (isset($r[0])){ return $r[0]; }
		else{ return array(); }
	}



	//========================================== count
	function count( $hash=array() ){

		$tmp_dump = 0;
		if ($this->dump==1){
			$tmp_dump = 1;
			$this->dump=0;
		}

		$tmp_loop = $this->select($hash, 0, 999999);
		$count = count($tmp_loop);

		if ($tmp_dump==1){
			$this->dump = 1;
			print "==============================<br />\n";
			print "count: {$count}<br />\n";
			print "==============================<br />\n";
		}
		return $count;
	}



	//========================================== insert
	function insert($hash,$flag='top'){

		if ($this->dump==1){
			print "============================== <br>INSERT : \n";
			$this->dump($hash);
		}

		// _check_column
		$this->_check_columns($hash);

		// lock
		$this->_lock()or die('Lock File Error. Server Busy. Please Reload after 1 minutes .');

		// _count_increment
		$num = $this->_count_increment();

		//make_data(insert)
		$primary_column_name = $this->primary[0];
		$write_data = '';
		foreach ( $this->columns as $k => $v ){
			if ( strcmp($primary_column_name,$v)==0){ $data = $num; }
			elseif ( isset($hash[$v]) ){ $data = $hash[$v]; }
			else{ $data = ''; }
			// encode
			$data = $this->_encode_csv($data);
			$write_data .= $data.'<>';
		}
		$write_data .= $data."\n";

//$this->dump($write_data);

		// write_data
		$pid = getmypid();
		$tmp_filename = "{$this->data_dir}/{$this->table_name}.{$this->ext}".$pid.time();
		$filename     = "{$this->data_dir}/{$this->table_name}.{$this->ext}";

		$fptmp =fopen($tmp_filename, 'w');

		if ($flag=='top'){ fwrite($fptmp, $write_data); }
		$fp =fopen($filename, 'r') or die("textdb:error cannot open count file ({$filename})");
		while (!feof($fp)) {
			$line = fgets($fp, 99999);	// 1行読み込み（最大99999bytes）
			fwrite($fptmp, $line);
		}
		if ($flag=='bottom'){ fwrite($fptmp, $write_data); }

		fclose($fp);
		fclose($fptmp);
		rename($tmp_filename, $filename);

		if ($this->dump==1){
			$this->dump($write_data);
		}

		// unlock
		$this->_unlock();
		return($num);
	}



	//========================================== find_or_create
	function find_or_create($hash){

		$r = array();
		$r = $this->select_one($hash);
//$this->dump($r);
		if (count($r)>0){
// $this->dump($r[$this->columns[0]]);
			return $r[$this->columns[0]];
		}
		else{
			$c = $this->insert($hash);
//$this->dump($c); die;
			return $c;
		}
	}



	//========================================== update
	function update($hash){

		$time_start=0; $time_end=0; $time_work=0;
		if ($this->dump==1){
			// 時間の計測 start
			$time_start=$this->_getmicrotime();
			print "============================== <br>UPDATE : \n";
			$this->dump($hash);
		}

		// _check_column
		$this->_check_columns($hash);

		// _check_primary_key
		$this->_check_primary_key($hash);

		// lock
		$this->_lock()or die('Lock File Error. Server Busy. Please Reload after 1 minutes .');


		// filename
		$pid = getmypid();
		$tmp_filename = "{$this->data_dir}/{$this->table_name}.{$this->ext}".$pid.time();
		$filename     = "{$this->data_dir}/{$this->table_name}.{$this->ext}";

		// make_data(update)
		$read_data_hash = array();
		$primary_column_name = $this->primary[0];
		$fp =fopen($filename, 'r') or die("textdb:error cannot open count file ({$filename})");
		while (!feof($fp)) {
			$line = fgets($fp, 99999);	// 1行読み込み（最大99999bytes）
			$h = $this->_read_data($line);
			if (strcmp($h[$primary_column_name],$hash[$primary_column_name]) == 0 ){ $read_data_hash = $h; break; }
		}

		foreach ($read_data_hash as $k => $v){
			$read_data_hash[$k] = preg_replace('/\<br \/\>/', "\n", $read_data_hash[$k]);
		}

//$this->dump($read_data_hash);

//$this->dump($hash);
		$num = $hash[$primary_column_name];
		$write_data = '';
//$this->dump($this->columns);
//$this->dump($hash);
		foreach ( $this->columns as $k => $v ){
//$this->dump($v);
			if ( strcmp($primary_column_name,$v)==0){ $data = $num; }

//OLD			elseif ( isset($hash[$v]) ){
			elseif ( array_key_exists($v,$hash) ){
				$data = $hash[$v];
				//$this->dump($data);
			}
			else{
				$read_data_hash[$v] = preg_replace("/\r\n/", "\n", $read_data_hash[$v]);
				$read_data_hash[$v] = preg_replace("/\r/", "\n", $read_data_hash[$v]);
				$data = $read_data_hash[$v];
				// print "【{$data}】";
			}
			$data = $this->_encode_csv($data);
			$write_data .= $data.'<>';
		}
		$write_data .= "\n";
//$this->dump($write_data); die;
		
		//OFF fclose($fp);

		fseek( $fp, 0, SEEK_SET);

		// write_data
		$primary_column_name = $this->primary[0];
		$fptmp =fopen($tmp_filename, 'w');

		//OFF $fp =fopen($filename, 'r') or die("textdb:error cannot open count file ({$filename})");
		while (!feof($fp)) {
			$line = fgets($fp, 99999);	// 1行読み込み（最大99999bytes）
			$h = $this->_read_data($line);
			if (strcmp($h[$primary_column_name],$hash[$primary_column_name]) == 0 ){ fwrite($fptmp, $write_data); }
			else{ fwrite($fptmp, $line); }
		}
		fclose($fp);
		fclose($fptmp);
		rename($tmp_filename, $filename);

		if ($this->dump==1){
			$this->dump( htmlspecialchars($write_data) );
			// 時間の計測 end
			$time_end=$this->_getmicrotime();
			$time_work=$time_end - $time_start;
			$this->dump("time:{$time_work}\n");
		}

		// unlock
		$this->_unlock();
		return($num);
	}



	//========================================== move_top
	function move_top($hash){

		if ($this->dump==1){
			print "============================== <br>MOVE_TOP : \n";
			$this->dump($hash);
		}

		// _check_column
		$this->_check_columns($hash);

		// _check_primary_key
		$this->_check_primary_key($hash);

		// lock
		$this->_lock()or die('Lock File Error. Server Busy. Please Reload after 1 minutes .');

		// filename
		$pid = getmypid();
		$tmp_filename = "{$this->data_dir}/{$this->table_name}.{$this->ext}".$pid.time();
		$filename     = "{$this->data_dir}/{$this->table_name}.{$this->ext}";

		$primary_column_name = $this->primary[0];

		$fp    = fopen($filename, 'r') or die("textdb:error cannot open count file ({$filename})");
		$fptmp = fopen($tmp_filename, 'w');

		// データ読み込み
		$h = array();
		$move_line = '';
		while (!feof($fp)) {
			$line = fgets($fp, 99999);
			$h = $this->_read_data($line);
			if (strcmp($h[$primary_column_name],$hash[$primary_column_name]) == 0 ){ $move_line = $line; break; }
		}

		// データ書き込み
		fseek( $fp, 0, SEEK_SET);
		fwrite($fptmp, $move_line);
		while (!feof($fp)) {
			$line = fgets($fp, 99999);
			$h = $this->_read_data($line);
			if (strcmp($h[$primary_column_name],$hash[$primary_column_name]) == 0 ){}
			else{ fwrite($fptmp, $line); }
		}

		fclose($fp);
		fclose($fptmp);
		rename($tmp_filename, $filename);

		// unlock
		$this->_unlock();
		
	}



	//========================================== delete
	function delete( $hash=array() ){
	
		if ($this->dump==1){
			print "============================== <br>DELETE : \n";
			$this->dump($hash);
		}

		if (count($hash) == 0){
			print "textdb ERROR : please set delete option.";
			return;
		}

		// _check_column
		$this->_check_columns($hash);

//OFF		// _check_primary_key
//OFF		$this->_check_primary_key($hash);

		// lock
		$this->_lock()or die('Lock File Error. Server Busy. Please Reload after 1 minutes .');

		// filename
		$pid = getmypid();
		$tmp_filename = "{$this->data_dir}/{$this->table_name}.{$this->ext}".$pid.time();
		$filename     = "{$this->data_dir}/{$this->table_name}.{$this->ext}";

		// delete_data
		$delete_sum = 0;
		$primary_column_name = $this->primary[0];
		$fptmp = fopen($tmp_filename, 'w');
		$fp    = fopen($filename, 'r') or die("textdb:error cannot open data file");
		while (!feof($fp)) {
			$line = fgets($fp, 99999);	// 1行読み込み（最大99999bytes）
			$h = $this->_read_data($line);

			$flag = false;
			$hash_total_no = count($hash);
			$ture_total_no = 0;

			foreach ($hash as $k => $v){
				if ( strcmp($h[$k],$hash[$k])==0 ){ $ture_total_no++; }
			}
			if ($hash_total_no==$ture_total_no){ $flag = true; }

			// OFF if (strcmp($h[$primary_column_name],$hash[$primary_column_name]) == 0 ){ $delete_sum++; }
			if ( $flag ){ $delete_sum++; }
			else{ fwrite($fptmp, $line); }
		}
		fclose($fp);
		fclose($fptmp);
		rename($tmp_filename, $filename);

		if ($this->dump==1){
			print "============================== <br>\n";
			print "DELETE {$delete_sum} data(s).<br>\n";
		}

		// unlock
		$this->_unlock();

		return($delete_sum);
	}



	//========================================== delete_all：データの全件削除 + autoincrement値リセット
	function delete_all(){

		// data
		$filename     = "{$this->data_dir}/{$this->table_name}.{$this->ext}";
		if ( is_file($filename) ){
			unlink ($filename);
			touch($filename);
		}

		// count
		$filename     = "{$this->data_dir}/{$this->table_name}.count.cgi";
		if ( is_file($filename) ){
			unlink ($filename);
		}


	}



	//========================================== _read_data
	function _read_data( $line='' ){

		$line = rtrim($line);
		$hash = array();

//		if (strcmp($line,'')==0){
//			return $false;
//		}

		$line_array = array();
		$line_array = explode('<>',$line);
		$i = 0;
		foreach ( $this->columns as $k => $v ){
			if (isset($line_array[$i])){ $hash[$v] = $line_array[$i]; }
			else{ $hash[$v] = null; }
			$i++;
		}
//$this->dump($hash);
		return $hash;
	}


	//========================================== _check_columns（カラムが存在するかどうか調べる）
	function _check_columns($hash){
		foreach ($hash as $key => $value){
			if ( $key == 'ORDER_BY' ){ continue; }
			if ( $key == 'SEARCH_MODE' ){ continue; }
			if ( $key == 'RELATION' ){ continue; }
			if (! in_array($key,$this->columns) ){ die("column '$key' is not find in table '{$this->table_name}'"); }
		}
	}


	//========================================== _check_primary_key（IDがセットされているかどうか調べる）
	function _check_primary_key($hash){

		if ( ! isset($hash[$this->columns[0]])){ die("please set 1st column '{$this->columns[0]}' value"); }
		else if (strcmp($hash[$this->columns[0]],'')==0){ die("please set 1st column '{$this->columns[0]}' value"); }
	}


	//========================================== dump
	function dump($data){
		if ( $this->dump_encoding_to != '' ){
			mb_convert_variables( $this->dump_encoding_to, 'auto', $data );
		}

		print "\n".'<pre style="text-align:left;">'."\n";
		print "==============================\n";
		print_r($data);
		print "</pre>";
	}

	//========================================== debug_mode：ダンプモードにする
	function dump_mode($dump_encoding_to=''){
		if ($dump_encoding_to != ''){ $this->dump_encoding_to=$dump_encoding_to; }
		$this->dump=1;
	}

	//========================================== debug_mode_off：ダンプモードを抜ける
	function dump_mode_off(){
		$this->dump=0;
	}


	//========================================== _getmicrotime：現在の時間をマイクロ秒単位で返す関数
	function _getmicrotime(){
		list($msec, $sec) = explode(" ", microtime());
		return ((float)$sec + (float)$msec);
	}

	//========================================== help：ヘルパーメソッド(insertのテンプレート出力)
	function help(){
		print<<<EOS
<pre>
========================================================================
■ textdb [version {$this->version}] HELP
========================================================================
■ データ構造
</pre>
<table border="1" style="font-size:smaller;">
<tr><th>カラム名</th><th>型（現バージョンでは未実装）</th><th>コメント</th></tr>
EOS;

		$i = 0;
		foreach($this->columns as $v){
			print "<tr><td>{$v}</td><td>{$this->types[$i]}</td><td>{$this->comments[$i]}</td></tr>\n";
			$i++;
		}


		print<<<EOS
</table>
<pre>
========================================================================
■ INSERT文例（コピーペーストしてPHPソースに使用してください。）

\$insert_id = \$db_{$this->table_name}->insert(array(

EOS;
		
		$max = 0;
		foreach($this->columns as $v){
			if($max < strlen($v)) $max = strlen($v);
		}
		foreach($this->columns as $v){
			print "\t'{$v}'".str_repeat(' ', $max - strlen($v))." => \$this->q['{$v}'],\n";
		}
		print<<<EOS
));
========================================================================

EOS;

print "</pre>";
	}
}
?>