<?php
// $Id: search.php,v 1.6 2006/10/14 12:51:45 ohwada Exp $

// 2006-10-14 K.OHWADA
// add get_query_utf8_urlencode()

// 2006-09-20 K.OHWADA
// fuzzy search
// add build_candidate_array() etc

// 2006-07-10 K.OHWADA
// this is new file
// porting from rssc_search_handler.php

//=========================================================
// Happy Linux Framework Module
// 2006-07-10 K.OHWADA
//=========================================================

define('HAPPY_LINUX_SEARCH_CODE_SQL_NO_CAN', 31);
define('HAPPY_LINUX_SEARCH_CODE_SQL_CAN',    32);
define('HAPPY_LINUX_SEARCH_CODE_SQL_MERGE',  33);

define('HAPPY_LINUX_SEARCH_CODE_HANKAKU',    35);
define('HAPPY_LINUX_SEARCH_CODE_ZENKAKU',    36);

// for Japanese EUC-JP
define('HAPPY_LINUX_SEARCH_ZENKAKU_EISU', '/\xA3[\xC1-\xFA]/');
define('HAPPY_LINUX_SEARCH_HANKAKU_EISU', '/[A-Za-z0-9]/');
define('HAPPY_LINUX_SEARCH_ZENKAKU_KANA', '/\xA5[\xA1-\xF6]/');
define('HAPPY_LINUX_SEARCH_HANKAKU_KANA', '/\x8E[\xA6-\xDF]/');


//=========================================================
// class happy_linux_search
//=========================================================
class happy_linux_search
{
// class
	var $_strings;
	var $_post;
	var $_system;

// post
	var $_post_action;
	var $_post_andor;
	var $_post_query;

// input param
	var $_min_keyword = 5;
	var $_flag_cabdicate      = true;
	var $_flag_cabdicate_once = false;

// result
	var $_query_array;
	var $_ignore_array;
	var $_candidate_array;
	var $_candidate_keyword_array;
	var $_merged_query_array;
	var $_mode_andor;
	var $_sel_and;
	var $_sel_or;
	var $_sel_exact;
	var $_sql_andor;
	var $_sql_query_array;
	var $_query_urlencode;
	var $_query_utf8_urlencode;
	var $_merged_urlencode;
	var $_merged_utf8_urlencode;

// local
	var $_time_start;
	var $_is_japanese;

	var $_LANG_ZENKAKU = 'zenkaku';
	var $_LANG_HANKAKU = 'hankaku';

//---------------------------------------------------------
// constructor
//---------------------------------------------------------
function happy_linux_search()
{
	$this->_strings =& happy_linux_strings::getInstance();
	$this->_post    =& happy_linux_post::getInstance();
	$this->_system  =& happy_linux_system::getInstance();
}

function &getInstance()
{
	static $instance;
	if (!isset($instance)) 
	{
		$instance = new happy_linux_search();
	}

	return $instance;
}

//---------------------------------------------------------
// get $_POST & $_GET
//---------------------------------------------------------
function get_post_get_action()
{
	$action = $this->_post->get_post_get_text('action');

	switch ( $action )
	{
		case 'results':
		case 'showall':
		case 'showallbyuser':
			$ret = $action;
			break;

		case 'search';
		default:
			$ret = 'search';
			break;
	}

	$this->_post_action = $ret;
	return $ret;
}

function get_post_get_andor()
{
	$andor = $this->_post->get_post_get_text('andor');

	switch ( $andor )
	{
		case 'OR';
		case 'exact';
			$ret = $andor;
			break;

		case 'AND';
		default:
			$ret = 'AND';
			break;
	}

	$this->_post_andor = $ret;
	return $ret;
}

function get_post_get_query()
{
	$this->_post_query = trim( $this->_post->get_post_get_text('query') );
	return $this->_post_query;
}

//--------------------------------------------------------
// parse query
//--------------------------------------------------------
function parse_query($query='', $andor='', $gpc=true)
{
	if ( $query && $gpc )
	{
		$query = $this->_strings->strip_slashes_gpc( $query );
	}
	elseif ( empty($query) )
	{
		$query = $this->_post_query;
	}

	if ( empty($andor) )
	{
		$andor = $this->_post_andor;
	}

	$this->_query_array             = array();
	$this->_ignore_array            = array();
	$this->_candidate_array         = array();
	$this->_candidate_keyword_array = array();
	$this->_merged_query_array      = array();
	$this->_mode_andor = '';
	$this->_sel_and    = '';
	$this->_sel_or     = '';
	$this->_sel_exact  = '';

	$this->_is_japanese = $this->_system->is_japanese();

	if ($query == '')
	{
		return false;
	}

	if ( ($andor != "OR") && ($andor != "exact") && ($andor != "AND") ) 
	{
		$andor = "AND";
	}

	if ($andor != 'exact') 
	{
		$query_han = $this->_convert_space_zen_to_han($query);
		$query_temp_arr = preg_split('/[\s,]+/', $query_han);

		foreach ($query_temp_arr as $q) 
		{
			$q = trim($q);

			if (strlen($q) >= $this->_min_keyword) 
			{
				$this->_query_array[] = $q;
				$this->_build_candidate($q);
			}
			else 
			{
				$this->_ignore_array[] = $q;
			}
		}

		if ($andor == 'OR') $this->_sel_or  = "selected='selected'";
		else                $this->_sel_and = "selected='selected'";
	}
	else 
	{
		$this->_query_array  = array( $query );
		$this->_sel_exact    = "selected='selected'";
	}

	$this->_mode_andor = $andor;

	if (count($this->_query_array) == 0)
	{
		return false;
	}

	$this->_merged_query_array = $this->_strings->merge_unique_array($this->_query_array, $this->_candidate_keyword_array);

	$this->_merged_urlencode      = $this->_strings->urlencode_from_array(      $this->_merged_query_array );
	$this->_merged_utf8_urlencode = $this->_strings->utf8_urlencode_from_array( $this->_merged_query_array );
	$this->_query_urlencode       = $this->_strings->urlencode_from_array(      $this->_query_array );
	$this->_query_utf8_urlencode  = $this->_strings->utf8_urlencode_from_array( $this->_query_array );

	return true;
}



//--------------------------------------------------------
// build query
//--------------------------------------------------------
function check_build_sql_query_array($query_array='', $candidate_keyword_array='', $andor='')
{
	if ( empty($candidate_keyword_array) )
	{
		$candidate_keyword_array = $this->_candidate_keyword_array;
	}

	if ( empty($query_array) )
	{
		$query_array = $this->_query_array;
	}

	if ( empty($andor) )
	{
		$andor = $this->_mode_andor;
	}

	$this->_sql_andor       = $andor;
	$this->_sql_query_array = $query_array;

	if ( is_array($candidate_keyword_array) && ( count($candidate_keyword_array) > 0 ) )
	{
		if (( count($query_array) == 1 )||( $andor == 'OR' ))
		{
			$this->_build_sql_query_array($candidate_keyword_array, $query_array, $andor);
			return HAPPY_LINUX_SEARCH_CODE_SQL_MERGE;
		}
		else
		{
			return HAPPY_LINUX_SEARCH_CODE_SQL_CAN;
		}
	}
	return HAPPY_LINUX_SEARCH_CODE_SQL_NO_CAN;
}

function _build_sql_query_array($query_array, $candidate_keyword_array, $andor)
{
	$this->_sql_andor       = 'OR';
	$this->_sql_query_array = $this->_strings->merge_unique_array($query_array, $candidate_keyword_array);
}

//--------------------------------------------------------
// build sql
//--------------------------------------------------------
function build_single_double_where($field_name, $query_array1, $query_array2=null, $andor='AND')
{
	$where  = '';
	$where1 = '';
	$where2 = '';

	if ( is_array($query_array1) && ( count($query_array1) > 0 ) )
	{
		$where1 = $this->build_single_where($field_name, $query_array1, $andor);
	}

	if ( is_array($query_array2) && ( count($query_array2) > 0 ) )
	{
		$where2 = $this->build_single_where($field_name, $query_array2, $andor);
	}

	if ( $where1 && $where2 )
	{
		$where = ' ( '.$where1.' OR '.$where2.' ) ';
	}
	elseif ( $where1 )
	{
		$where = $where1;
	}
	elseif ( $where2 )
	{
		$where = $where2;
	}

	return $where;
}

function build_multi_where($field_name_array, $query_array, $andor='AND')
{
	$where = '';
	$arr   = array();

	if ( is_array($field_name_array) && ( count($field_name_array) > 0 ) )
	{
		foreach( $field_name_array as $name )
		{
			$arr[] = $this->_build_single_where($name, $query_array, $andor);
		}
	}

	if ( count($arr) > 0 )
	{
		$where  = ' ( ';
		$where .= implode(' OR ', $arr);
		$where .= ' ) ';
	}

	return $where;
}

function build_single_where($field_name, $query_array, $andor='AND')
{
	$where = '';

	if ( is_array($query_array) )
	{
		$count = count($query_array);

		if ( $count > 0 )
		{
			$q = addslashes( $query_array[0] );
			$where .= " ( ".$field_name." LIKE '%".$q."%' ";

			for ($i=1; $i<$count; $i++)
			{
				$q = addslashes( $query_array[$i] );
				$where .= $andor." ";
				$where .= $field_name." LIKE '%".$q."%' ";
			}

			$where .= ") ";
		}
	}

	return $where;
}

//--------------------------------------------------------
// set param
//--------------------------------------------------------
function set_min_keyword($value)
{
	$this->_min_keyword = intval($value);
}

function set_flag_cabdicate($value)
{
	$this->_flag_cabdicate = intval($value);
}

function set_flag_cabdicate_once($value)
{
	$this->_flag_cabdicate_once = intval($value);
}

function set_lang_zenkaku($value)
{
	$this->_LANG_ZENKAKU = $value;
}

function set_lang_hankaku($value)
{
	$this->_LANG_HANKAKU = $value;
}

//--------------------------------------------------------
// get param
//--------------------------------------------------------
function &get_query_array($format='s')
{
	if ( $format )
	{
		$arr =& $this->_strings->sanitize_array_text($this->_query_array);
	}
	else
	{
		$arr =& $this->_query_array;
	}
	return $arr;
}

function &get_ignore_array($format='s')
{
	if ( $format )
	{
		$arr =& $this->_strings->sanitize_array_text($this->_ignore_array);
	}
	else
	{
		$arr =& $this->_ignore_array;
	}
	return $arr;
}

function &get_candidate_array($format='s')
{
	if ( $format )
	{
		$arr =& $this->_strings->sanitize_array_text($this->_candidate_array);
	}
	else
	{
		$arr =& $this->_candidate_array;
	}
	return $arr;
}

function &get_candidate_keyword_array()
{
	$arr =& $this->_candidate_keyword_array;
	return $arr;
}

function get_count_query_array()
{
	if ( is_array($this->_query_array) )
	{
		return count($this->_query_array);
	}
	return false;
}

function get_count_ignore_array()
{
	if ( is_array($this->_ignore_array) )
	{
		return count($this->_ignore_array);
	}
	return false;
}

function get_count_candidate_array()
{
	if ( is_array($this->_candidate_array) )
	{
		return count($this->_candidate_array);
	}
	return false;
}

function get_query_urlencode()
{
	return $this->_query_urlencode;
}

function get_query_utf8_urlencode()
{
	return $this->_query_utf8_urlencode;
}

function get_merged_query_array()
{
	return $this->_merged_query_array;
}

function get_merged_urlencode()
{
	return $this->_merged_urlencode;
}

function get_merged_utf8_urlencode()
{
	return $this->_merged_utf8_urlencode;
}

function get_andor()
{
	return $this->_mode_andor;
}

function get_and()
{
	return $this->_sel_and;
}

function get_or()
{
	return $this->_sel_or;
}

function get_exact()
{
	return $this->_sel_exact;
}

function get_sql_andor()
{
	return $this->_sql_andor;
}

function get_sql_query_array()
{
	return $this->_sql_query_array;
}

//=========================================================
// Private
//=========================================================

//--------------------------------------------------------
// convert for Japanese EUC-JP
// porting from suin's search <http://suin.jp/>
//--------------------------------------------------------
function _convert_space_zen_to_han($str)
{
	if ( !$this->_is_japanese || !function_exists('mb_convert_kana') )
	{	return $str; 	}

	return mb_convert_kana($str, "s");
}

function _build_candidate($q)
{
	if ( !$this->_flag_cabdicate )
	{	return; 	}

	if ( !$this->_is_japanese || !function_exists('mb_convert_kana') )
	{	return; 	}

// Zenkaku Eisu
// option a: Convert "zen-kaku" alphabets and numbers to "han-kaku"
	if ( preg_match(HAPPY_LINUX_SEARCH_ZENKAKU_EISU, $q) )
	{
		$keyword = mb_convert_kana($q, 'a');
		$this->_set_candidate_array($q, $keyword, HAPPY_LINUX_SEARCH_CODE_HANKAKU);

		if ($this->_flag_cabdicate_once)
		{	return; 	}
	}

// Hankaku Eisu	
// option A: Convert "han-kaku" alphabets and numbers to "zen-kaku" 
// (Characters included in "a", "A" options are U+0021 - U+007E excluding U+0022, U+0027, U+005C, U+007E)
	if ( preg_match(HAPPY_LINUX_SEARCH_HANKAKU_EISU, $q) )
	{
		$keyword = mb_convert_kana($q, 'A');
		$this->_set_candidate_array($q, $keyword, HAPPY_LINUX_SEARCH_CODE_ZENKAKU);

		if ($this->_flag_cabdicate_once)
		{	return; 	}
	}

// Zenkaku Katakana	
// option k: Convert "zen-kaku kata-kana" to "han-kaku kata-kana"
// option c: Convert "zen-kaku kata-kana" to "zen-kaku hira-kana"
	if ( preg_match(HAPPY_LINUX_SEARCH_ZENKAKU_KANA, $q) )
	{
		$keyword_k = mb_convert_kana($q, 'k');
		$this->_set_candidate_array($q, $keyword_k, HAPPY_LINUX_SEARCH_CODE_HANKAKU);

		if ($this->_flag_cabdicate_once)
		{	return; 	}

		$keyword_c = mb_convert_kana($q, 'c');
		$this->_set_candidate_array($q, $keyword_c, HAPPY_LINUX_SEARCH_CODE_ZENKAKU);

		if ($this->_flag_cabdicate_once)
		{	return; 	}
	}

// Hankaku Katakana	
// option K: Convert "han-kaku kata-kana" to "zen-kaku kata-kana"
// option H: Convert "han-kaku kata-kana" to "zen-kaku hira-kana"
// option V: Collapse voiced sound notation and convert them into a character. Use with "K","H"
	if ( preg_match(HAPPY_LINUX_SEARCH_HANKAKU_KANA, $q) )
	{
		$keyword_kv = mb_convert_kana($q, 'KV');
		$this->_set_candidate_array($q, $keyword_kv, HAPPY_LINUX_SEARCH_CODE_ZENKAKU);

		if ($this->_flag_cabdicate_once)
		{	return; 	}

		$keyword_hv = mb_convert_kana($q, 'HV');
		$this->_set_candidate_array($q, $keyword_hv, HAPPY_LINUX_SEARCH_CODE_ZENKAKU);

		if ($this->_flag_cabdicate_once)
		{	return; 	}
	}

// option h: Convert "zen-kaku hira-kana" to "han-kaku kata-kana"
// option C: Convert "zen-kaku hira-kana" to "zen-kaku kata-kana"
	$keyword_h  = mb_convert_kana($q, 'h');
	$keyword_cc = mb_convert_kana($q, 'C');

	if ( $q != $keyword_h )
	{
		$this->_set_candidate_array($q, $keyword_h, HAPPY_LINUX_SEARCH_CODE_HANKAKU);

		if ($this->_flag_cabdicate_once)
		{	return; 	}
	}

	if ( $q != $keyword_cc )
	{
		$this->_set_candidate_array($q, $keyword_cc, HAPPY_LINUX_SEARCH_CODE_ZENKAKU);

		if ($this->_flag_cabdicate_once)
		{	return; 	}
	}
}

function _set_candidate_array($q, $keyword, $type)
{
	if (strlen($keyword) < $this->_min_keyword)
	{	return; 	}

	if ( $q == $keyword )
	{	return; 	}

	$this->_candidate_keyword_array[] = $keyword;

	if ($type == HAPPY_LINUX_SEARCH_CODE_ZENKAKU)
	{
		$this->_candidate_array[] = array(
			'keyword' => $keyword,
			'type'    => HAPPY_LINUX_SEARCH_CODE_ZENKAKU,
			'lang'    => $this->_LANG_ZENKAKU,
		);
	}
	else
	{
		$this->_candidate_array[] = array(
			'keyword' => $keyword,
			'type'    => HAPPY_LINUX_SEARCH_CODE_HANKAKU,
			'lang'    => $this->_LANG_HANKAKU,
		);
	}
}

//----- class end -----
}

?>