Location: PHPKode > scripts > TextIndexer > indexer/class.indexerDB.php
<?php 
/**
 * Indexer result
 * @package Indexer
 * @author Oliver Kührig
 */
class IndexerResult {
	var $id;
	var $keys;
	var $score;
	var $text;
	
	function IndexerResult() {
		$this->id = 0;
		$this->keys = array();
		$this->score = 0;
		$this->text = "";
	}
}

/**
 * Indexer generates indexfiles
 * @package Indexer
 * @author Oliver Kührig
 * @version 1.0, 2004-10-07
 */
class IndexerDB {
	
    /**
     * id of the input object
     * @var integer
     */
	var $id = 0;
	
    /**
     * array container for the type and values
     * @var array
     */
	var $arrFields = array();
	
    /**
     * array container for the parameters
     * @var array
     */
	var $arrParams = array();
	
    /**
     * array container for the relevant words
     * @var array
     */
	var $arrWords = array();
	
    /**
     * array container for the index
     * @var array
     */
	var $arrIndex = array();
	
    /**
     * array container for the blacklist words
     * @var array
     */
	var $arrBlackList = array();
	
    /**
     * log file
     * @var string
     */
	var $logFile = "idx.log";
	
    /**
     * path to the indexer
     * @var string
     */
	var $pathToIndexer = "./";
	
    /**
     * path to the object files
     * @var string
     */
	var $pathObjectFiles = "./object/";
	
    /**
     * path to the index files
     * @var string
     */
	var $pathIndexFiles = "./index/";
	
    /**
     * searchword
     * @var string
     */
	var $searchword = "";
	
    /**
     * count searchwords
     * @var integer
     */
	var $countSearchwords = 0;
	
    /**
     * returns the results beginning with offset
     * @var integer
     */
	var $searchOffset = 0;
	
    /**
     * number of objects to return
     * @var integer
     */
	var $searchRowCount = 0;
	
    /**
     * minimum percent value to add to the key words
     * @var integer
     */
	var $textSimilarityInPercent = 100;
	
    /**
     * text length to return in the resulting search array
     * @var integer
     */
	var $textLength = 120;
	
    /**
     * negative offset in the text  to return in the resulting search array
     * @var integer
     */
	var $textOffset = 30;
	
    /**
     * delimiter for the returning text at the beginning and the end
     * @var string
     */
	var $textDelimiter = "...";
	
    /**
     * negative offset in the text  to return in the resulting search array
     * @var string
     */
	var $textHighlight = '<span style="border-bottom: 1px dotted;"><strong>\1</strong></span>';
	
    /**
     * array container for the matching index lines
     * @var array
     */
	var $arrSearchIds = array();
	
    /**
     * count all results without limit
     * @var integer
     */
	var $countAllResults = 0;
	
    /**
     * array container for the resulting search words
     * @var array
     */
	var $arrResult = array();
	
    /**
     * start time
     * @var float
     */
	var $scriptStartTime = 0;
	
    /**
     * stop time
     * @var float
     */
	var $scriptStopTime = 0;
	
    /**
     * array unicode to htmlchar matching table
     * @var array
     */
	var $unicode_array = array(
		"&#8211;" => "-",
		"&#8212;" => "-",
		"–" => "-",
		"&#8216;" => "'",
		"’" => "'",
		"&#8217;" => "'",
		"‘" => "'",
		"&#8230;" => "...",
		"…" => "...",
		"“" => "\"",
		"&#8220;" => "\"",
		"”" => "\"",
		"&#8221;" => "\"",
		"„" => "\"",
		"&#8222;" => "\"",
	);

	/**
     * Database connection object
     * @var DatabaseMysql
     */
	var $db_connection = null;
	/**
     * Database table holding the index
     * @var String
     */
	var $dbIndexTable = "indexer";
	
	
	// ********************************************************************************************************
	
	/**
     * Initialize the class so that the data is in a known state.
     * @access public
     * @return void
 	 * @param DatabaseMysql $objDatabaseMysql database connection object
	*/
	function IndexerDB($objDatabaseMysql) {
		if ($objDatabaseMysql) $this->db_connection = $objDatabaseMysql;
	}
	
    /**
     * convert the special html code in normal chars
     * @access private
     * @param string $string input string
     * @return string $trans_tbl converted string
     */
	function unhtmlentities($string) {
		$trans_tbl = get_html_translation_table(HTML_ENTITIES);
		$trans_tbl = array_flip($trans_tbl);
		$trans_tbl = strtr($string, $trans_tbl);
		return $trans_tbl;
	}
	
    /**
     * emilinates the returns and convert the text
     * @access private
     * @param string $text input string
     * @return string $text converted text
     */
	function convertString2Line($text) {
		$text = str_replace("\r\n", "\n", $text);
		$text = str_replace("\n", "#$#", $text);
		
		$text = strtr($text, $this->unicode_array);
		
		$text = $this->unhtmlentities($text);
		return $text;
	}
	
    /**
     * emilinates the special returns
     * @access private
     * @param string $text input string
     * @return string $text converted text
     */
	function convertLine2String($text) {
		$text = str_replace("#$#", "\n", $text);
		return $text;
	}
	
    /**
     * load the object file and return the data
     * @access private
     * @param integer $id unique id to load a file
     * @return array $arrData content of the object file as array by line, if file does not exists, null will be returned
     */
	function loadObjectFile($id) {
		$filename = "obj".$id.".txt";
		$arrData = null;
		if (file_exists($this->pathToIndexer.$this->pathObjectFiles.$filename)) {
			$arrData = file($this->pathToIndexer.$this->pathObjectFiles.$filename);
		}
		
		return $arrData;
	}
	
    /**
     * write the object file
     * @access private
     * @return void
     */
	function writeObjectFile() {
		$filename = "obj".$this->id.".txt";
		$filename = $this->pathToIndexer.$this->pathObjectFiles.$filename;
		
		// Sichergehen, dass die Datei existiert und beschreibbar ist
		if (is_writable( $this->pathToIndexer.$this->pathObjectFiles)) {
			$handle = fopen($filename, "w");
			
			// write Parameters
			for ($i=0; $i<count($this->arrParams); $i++) {
				fwrite($handle, "#param#".$this->arrParams[$i][0]."|".$this->arrParams[$i][1]."\n");
			}
			
			// write Fields
			for ($i=0; $i<count($this->arrFields); $i++) {
				fwrite($handle, $this->arrFields[$i][0]."|".$this->arrFields[$i][1]."\n");
			}
			
			fclose($handle);
			
		} else {
			$this->logging("ERROR:".$filename." not writable");
		}
	}
	
    /**
     * delete the object file
     * @access private
     * @return void
     */
	function deleteObjectFile() {
		$filename = "obj".$this->id.".txt";
		$filename = $this->pathToIndexer.$this->pathObjectFiles.$filename;
		
		if (file_exists($filename)) {
			if (!unlink($filename)) {
				$this->logging("ERROR:".$filename." not deletable");
			}
		}
	}
	
    /**
     * loads the indexfile and edit the lines
     * @access private
     * @return void
     */
	function editIndexFile() {
		$this->arrIndex = array();
		
		if (!$this->db_connection) {
			$this->logging("ERROR: no database connection");
		} else {
			// insert new keywords
			$this->db_connection->execute("DELETE FROM `".$this->dbIndexTable."` WHERE id='".$this->id."'");
			foreach ($this->arrWords as $key=>$count) {
				if ($count>0) {
					$this->db_connection->execute("INSERT INTO `".$this->dbIndexTable."` (word, count, id) VALUES ('".$key."', ".$count.", '". $this->id . "')");
				}
			}
		} 
	}
	
    /**
     * empty the indexfile
     * @access public
     * @return void
     */
	function emptyIndexFile() {
		if ($this->db_connection->execute("TRUNCATE `".$this->dbIndexTable."`")) {
			$this->logging("INDEX: table ".$this->dbIndexTable." emptied");
		} else {
			$this->logging("ERROR: table ".$this->dbIndexTable." not existing");
		}
	}
	
    /**
     * loads the blacklist into the array arrBlackList
     * @access private
     * @return void
     */
	function loadBlackList() {
		$filename = $this->pathToIndexer."blacklist.php";
		if (file_exists($filename)) {
			$handle = fopen ($filename, "r");
			while (!feof($handle)) {
				$this->arrBlackList[] = chop(fgets($handle, 4096));
			}
			fclose ($handle);
		} else {
			$this->logging("ERROR:".$filename." not existing");
		}
	}
	
    /**
     * convert the line of the indexfile
     * @access private
     * @param string $line textline from indexfile
     * @return string $line edited textline from indexfile
     */
	function deleteIdFromArray($arr) {
		$arrTemp = array();
		for ($j=0; $j<count($arr); $j++) {
			if ($arr[$j] != $this->id) $arrTemp[] = $arr[$j];
		}
		return $arrTemp;
	}
	
	
    /**
     * clean the words, by removing 'dirty' chars
     * @access private
     * @param string $strWord word to examine
     * @return string $strWord clean word or empty string
     */
	function cleanWord($strWord) {
		// entferne alle Zeichen ausser Buchstaben und Bindestrich
		$strWord = preg_replace("/(.*)&#(.*);(.*)/i", "\\1\\3", $strWord);	// entferne HTML-Hex-Wörter
		preg_match_all("/[\w-]/", $strWord, $arrText);
		$strWord = implode("", $arrText[0]);
		
		// lösche Wort, falls in Blacklist
		if (in_array(strtolower($strWord), $this->arrBlackList)) {
			$strWord = "";
		}
		
		return $strWord;
	}
	
    /**
     * parse one line to examine the words and insert them into the arrWords-array
     * @access private
     * @param string $text complete textline
     * @return void
     */
	function parseLine($text) {
		// convert text
		$text = strtolower($text);
		$text = $this->unhtmlentities($text);
		$text = str_replace("#$#", " ", $text);
		$text = str_replace(".", " ", $text);
		$text = str_replace(",", " ", $text);
		//$text = str_replace(";", " ", $text);
		$text = str_replace(":", " ", $text);
		//$text = preg_replace("/(.*)&#(.*);(.*)/i", "\\1 \\3", $text);	// entferne HTML-Hex-Wörter
		
		// explode text into array
		$arr = explode(" ", $text);
		for ($i=0; $i<count($arr); $i++) {
			if (strlen($arr[$i]) > 3) {
				$strWord = $this->cleanWord($arr[$i]);
				if ($strWord != "") 
					if (isset($this->arrWords[$strWord])) $this->arrWords[$strWord]++; else $this->arrWords[$strWord] = 1;
			}
		}
		
		ksort($this->arrWords);
	}
	
    /**
     * parse the object for relevant words to add to the index
     * @access private
     * @return void
     */
	function parseRelevantWords() {
		$this->arrWords = array();
		
		for ($i=0; $i<count($this->arrFields); $i++) {
			$this->parseLine($this->arrFields[$i][1]);
		}
		//print_r($this->arrWords);
	}
	
    /**
     * fill the array arrFields
     * @access public
     * @param string $type type of the object
     * @param string $value value of the object
     * @return void
     */
	function addField($type, $value) {
		$value = $this->convertString2Line($value);
		//echo $value."<br>";
		$this->arrFields[] = array($type, $value);
	}
	
    /**
     * fill the array arrParams
     * @access public
     * @param string $type type of the object
     * @param string $value value of the object
     * @return void
     */
	function addParameter($type, $value) {
		$value = $this->convertString2Line($value);
		//echo $value."<br>";
		$this->arrParams[] = array($type, $value);
	}
	
	
	
	/**
	* removes the index for the setted id
	* @return boolean $success returns true or false, if operation went correct or not.
	* @access public
	*
	*/
	function removeIndex() {
		$this->startTimer();
		
		$this->deleteObjectFile();
		$this->editIndexFile();
		
		$duration = $this->stopTimer(2);
		//echo "Removing ID: ".$this->id." (Duration: ".$duration." sec)<br>";
		$this->logging("REMOVE:".$this->id." DURATION:".$duration);
	}
	
    /**
     * start the indexing process
     * @access public
     * @return void
     */
	function index() {
		$this->startTimer();
		
		$this->writeObjectFile();
		$this->loadBlackList();
		$this->parseRelevantWords();
		$this->editIndexFile();
		
		$duration = $this->stopTimer(2);
		$this->logging("INDEX:".$this->id." DURATION:".$duration);
		
		//echo "Indexing ID: ".$this->id." (Duration: ".$duration." sec)<br>";
		//echo "Mem: ".memory_get_usage()."<br>";
	}
	
	
	// ********************************************************************************************************
	
	
    /**
     * append the text in the logfile
     * @access private
     * @param string $text text to append to the logfile
     * @return void
     */
	function logging($text) {
		$file = $this->pathToIndexer.$this->pathIndexFiles.$this->logFile;
		$now = date ("Y-m-d H:i:s");
		
		$line = $now." ".$text;
		
		$handle = @fopen ($file, "a");
		@fwrite($handle, $line."\n");
		@fclose($handle);
	}
	
    /**
     * set the current id
     * @access public
     * @param integer $id id of the object
     * @return void
     */
	function setId($id) {
		$this->id = $id;
	}
	
    /**
     * set the searchword
     * @access public
     * @param string $val searchword
     * @return void
     */
	function setSearchword($val) {
		$arrVal = explode(" ", $val);
		for ($i=0; $i<count($arrVal); $i++) {
			if ($this->searchword == "") $this->searchword = chop(trim($arrVal[$i]));
			else $this->searchword .= "&&".chop(trim($arrVal[$i]));
		}
		$this->countSearchwords = count(explode("&&", $this->searchword));
	}
	
    /**
     * set the parameter to filter the search result
     * @access public
     * @param string $type type
     * @param string $val val
     * @return void
     */
	function setParameter($type, $val) {
		$this->arrParams[] = array("type"=>$type, "val"=>$val);
	}
	
    /**
     * set the text similarity in percent
     * @access public
     * @param integer $val percent value
     * @return void
     */
	function setTextSimilarityInPercent($val) {
		$this->textSimilarityInPercent = $val;
	}
	
    /**
     * set the cutted text length in the returning array
     * @access public
     * @param integer $val length of the cutted text
     * @return void
     */
	function setTextLength($val) {
		$this->textLength = $val;
	}
	
    /**
     * set the offset of the keyword in the cutted text
     * @access public
     * @param integer $val offset of th keyword in the cutted text
     * @return void
     */
	function setTextOffset($val) {
		$this->textOffset = $val;
	}
	
    /**
     * set the delimiter of the cutted text at the beginning and the end
     * @access public
     * @param string $val string of delimiter
     * @return void
     */
	function setTextDelimiter($val) {
		$this->textDelimiter = $val;
	}
	
    /**
     * set HTML Tag around the keyword in the text
     * @access public
     * @param string $val highlighting tag
     * @return void
     */
	function setTextHighlight($val) {
		$this->textHighlight = $val;
	}
	
    /**
     * set path to indexer
     * @access public
     * @param string $val path to the indexer
     * @return void
     */
	function setPathToIndexer($val) {
		if (substr ($val, -1) != "/")  $val .= "/";
		$this->pathToIndexer = $val;
	}
	
    /**
     * set searchOffset and searchRowCount to make the resultset smaller
     * @access public
     * @param integer $offset start of returning the result
     * @param integer $rowcount number of results to return
     * @return void
     */
	function setLimit($offset, $rowcount) {
		$this->searchOffset = $offset;
		$this->searchRowCount = $rowcount;
	}
	
	
	// ********************************************************************************************************
	
	
    /**
     * load the text return the text clipping
     * @access private
     * @param integer $id unique id
     * @param array $arrKeys key string in array
     * @return string $snippet small text clipping
     */
	function getTextClipping($arrObjectFile, $arrKeys) {
		//print_r($arrObjectFile);
		
		$text = "";
		for ($i=0; $i<count($arrObjectFile); $i++) {
			if (!ereg("#param#", $arrObjectFile[$i])) {
				// hole erste Pos von der pipe
				$pos = strlen($arrObjectFile[$i]) - strlen(stristr($arrObjectFile[$i], "|"));
				$myText = substr($arrObjectFile[$i], $pos+1);
				
				$text .= $this->convertLine2String(chop($myText))."\n";
			}
		}
		// suche erstes Vorkommen des zu suchenden Wortes
		$pos = $this->str_contains($text, $arrKeys[0], true);
		for ($i=1; $i<count($arrKeys); $i++) {
			$newPos = $this->str_contains($text, $arrKeys[$i], true);
			if ($newPos < $pos) $pos = $newPos;
		}
		
		$start = 0;
		if ($pos) {
			if ($pos > $this->textOffset) {
				$start = $pos - $this->textOffset;
			}
		}
		$stop = min($start + $this->textLength, strlen($text));
		
		$snippet = $this->strClippingWholeWords($text, $start, $stop, $this->textDelimiter);
		$snippet = $this->str_highlight($snippet, $arrKeys, $this->STR_HIGHLIGHT_SIMPLE, $this->textHighlight);
		
		return $snippet;
	}
	
    /**
     * build object from the array arrSearchIds
     * @access private
     * @return void
     */
	function buildObjects() {
		foreach ($this->arrSearchIds as $id=>$arrValue) {
			$arrObjectFile = $this->loadObjectFile($id);
			if ($arrObjectFile != null) {
				$filter = true;
				if (count($this->arrParams) > 0) {
					for ($i=0; $i<count($this->arrParams); $i++) {
						$found = false;
						for ($j=0; $j<count($arrObjectFile); $j++) {
							$line = $arrObjectFile[$j];
							if (ereg("#param#", $line)) {
								$line = substr($line, 7);
								$arrCurrentParam = explode("|", $line);
								if (($arrCurrentParam[0] == $this->arrParams[$i]['type']) and (chop($arrCurrentParam[1]) == $this->arrParams[$i]['val'])) $found = true;
							}
						}
						$filter = $filter && $found;
					}
				}
				if ($filter) {
					$obj = new IndexerResult();
					$obj->id = $id;
					$obj->keys = $arrValue['key'];
					$obj->score = $arrValue['score'];
					$obj->text = $this->getTextClipping($arrObjectFile, $arrValue['key']);
					
					$this->arrResult[] = $obj;
				}
			}
		}
		
		$this->countAllResults = count($this->arrResult);
		
		// limit
		if (($this->searchOffset !=0) or ($this->searchRowCount !=0)) {
			$this->arrResult = array_slice($this->arrResult, $this->searchOffset, $this->searchRowCount);
		}
	}
	
    /**
     * eliminate ids, which are not in all searchwords
     * @access private
     * @return void
     */
	function chooseSearchIds() {
		// remove ids
		//print_r($this->arrSearchIds);
		foreach ($this->arrSearchIds as $id=>$arrValue) {
			//echo count($arrValue['count'])."<br>";
			if (count($arrValue['count']) != $this->countSearchwords) {
				// if not all searchwords match to this id -> remove
				unset($this->arrSearchIds[$id]);
			}
		}
		arsort($this->arrSearchIds);
		
		//print_r($this->arrSearchIds);
	}
	
    /**
     * fill the keywordline into array
     * @access private
     * @return void
     */
	function fillSearchIds($word, $percent, $intSearchword) {
		$sql = "SELECT * from `".$this->dbIndexTable."` WHERE word = '".$word . "'";
		$arrRes = $this->db_connection->select($sql);
		for ($j=0; $j<count($arrRes); $j++) {
			if (isset($this->arrSearchIds[$arrRes[$j]['id']])) {
				$myScore = $this->arrSearchIds[$arrRes[$j]['id']]['score'] + ($percent*$arrRes[$j]['count']);
				$myKey = $this->arrSearchIds[$arrRes[$j]['id']]['key'];
				$myKey[] = $arrRes[$j]['word'];
				$myCount = $this->arrSearchIds[$arrRes[$j]['id']]['count'];
				if (!in_array($intSearchword, $myCount)) $myCount[] = $intSearchword;
			} else {
				$myScore = $percent*$arrRes[$j]['count'];
				$myKey = array();
				$myKey[] = $arrRes[$j]['word'];
				$myCount = array();
				$myCount[] = $intSearchword;
			}
			$this->arrSearchIds[$arrRes[$j]['id']] = array("score"=>$myScore, "key"=>$myKey, "count"=>$myCount);
		}
	}

    /**
     * get the percent of similarity
     * @access private
     * @return float $prozent percent of similarity
     */
	function getPercentForKeyword($key, $searchword) {
		if ($this->textSimilarityInPercent < 100) {
			if (strstr(strtolower($searchword), $key)) $prozent = 100; 
			else $prozent = $this->get_lcs(strtolower($searchword), $key);
		} else {
			if (ereg(strtolower($searchword), $key)) $prozent = 100; else $prozent = 0;
		}
		return $prozent;
	}
	
    /**
     * loads the indexfile into array
     * @access private
     * @return void
     */
	function loadIndexFileForSearching() {
		$this->arrIndex = array();
		
		$arrSearchwords = explode("&&", $this->searchword);
		
		$offset = 0;
		$strWhere = "";
    for ($i=0; $i<count($arrSearchwords); $i++) {
      $offLen = max(3, ceil(strlen($arrSearchwords[$i])/3));
      if ($strWhere) $strWhere .= " OR ";
      $strWhere .= " word LIKE '".substr($arrSearchwords[$i], 0, $offLen)."%'";
    }
    if ($strWhere) $strWhere = "WHERE ".$strWhere;
    //echo $strWhere;
		do {
			//$this->db_connection->startTimer();
      $sql = "SELECT DISTINCT word FROM `".$this->dbIndexTable."` ".$strWhere. " ORDER BY id LIMIT ".$offset . ", 200";
			//echo $sql;
			$arrRes = $this->db_connection->select($sql);
      $counter = 0;
			//echo $this->db_connection->stopTimer()." nach select<br>";
			//$this->db_connection->startTimer();
			for ($j=0; $j<count($arrRes); $j++) {
				for ($i=0; $i<count($arrSearchwords); $i++) {
					$percent = $this->getPercentForKeyword($arrRes[$j]['word'], $arrSearchwords[$i]);
					//echo $arrRes[$j]['word'].": $percent<br />";
					if ($percent >= $this->textSimilarityInPercent) 
						$this->fillSearchIds($arrRes[$j]['word'], $percent, $i);
				}
				$counter++;
			}
			//echo $this->db_connection->stopTimer()." nach for<br>";
			$offset = $offset + 200;
		} while (count($arrRes) == 200);
		//print_r($this->arrSearchIds);
	}
	
    /**
     * get count of all results without limit
     * @access public
     * @return integer $this->countAllResults number of all results without limit
     */
	function getCountAllResults() {
		return $this->countAllResults;
	}
	
    /**
     * start the searching process
     * @access public
     * @param string $searchword set the searchword
     * @return array $this->arrSearchIndex search result. 2D-Assozitive array of assoziations 'score' (the hitting percentage), 'key' (the founded word), 'id' (the id of the object), 'text' (a text snippet where the founded word is in)
     */
	function search($searchword="") {
		$this->arrResult = array();
		$this->startTimer();
		if ( (trim($searchword) != "") || ($this->searchword != "") ) {
			$this->setSearchword($searchword);
		  //echo date("H:i:s")."<br>";
			$this->loadIndexFileForSearching();
		  //echo date("H:i:s") . " loadIndexFileForSearching<br>";
			$this->chooseSearchIds();
		  //echo date("H:i:s"). " chooseSearchIds<br>";
			$this->buildObjects();
		  //echo date("H:i:s"). " buildObjects<br>";
			
			$duration = $this->stopTimer(2);
			//echo "Search for '".$this->searchword."' (Duration: ".$duration." sec)<br>";
			$this->logging("SEARCH:".$this->searchword." DURATION:".$duration);
		}
		//print_r($this->arrResult);
		return $this->arrResult;
	}
	
	
	// ********************************************************************************************************
	
	
	/**
	* @access private
	*
	*/	
	function LCS_Length($s1, $s2) {
		$m = strlen($s1);
		$n = strlen($s2);
		
		//this table will be used to compute the LCS-Length, only 128 chars per string are considered
		$LCS_Length_Table = array(array(128),array(128));
		
		
		//reset the 2 cols in the table
		for($i=1; $i < $m; $i++) $LCS_Length_Table[$i][0]=0;
		for($j=0; $j < $n; $j++) $LCS_Length_Table[0][$j]=0;
		
		for ($i=1; $i <= $m; $i++) {
			for ($j=1; $j <= $n; $j++) {
				if ($s1[$i-1]==$s2[$j-1]) {
					$LCS_Length_Table[$i][$j] = $LCS_Length_Table[$i-1][$j-1] + 1;
				} else {
					$help1 = (isset($LCS_Length_Table[$i-1][$j])) ? $LCS_Length_Table[$i-1][$j] : 0;
					$help2 = (isset($LCS_Length_Table[$i][$j-1])) ? $LCS_Length_Table[$i][$j-1] : 0;
					if ($help1 >= $help2) {
						$LCS_Length_Table[$i][$j] = $help1;
					} else {
						$LCS_Length_Table[$i][$j] = $help2;
					}
				}
			}
		}
		return $LCS_Length_Table[$m][$n];
	}
	
	/**
	* @access private
	*
	*/	
	function str_lcsfix($s) {
		$s = str_replace(" ","",$s);
		$s = ereg_replace("[éèêëËÊÉÈ]","e", $s);
		$s = ereg_replace("[àáâãäåÄÅÃÂÁÀ]","a", $s);
		$s = ereg_replace("[ìíîïÏÎÍÌ]","i", $s);
		$s = ereg_replace("[òóôõöÖÕÔÓ]","o", $s);
		$s = ereg_replace("[ÜÛÚÙùúûü]","u", $s);
		$s = ereg_replace("[Ç]","c", $s);
		return $s;
	}
	
	/**
	* @access private
	*
	*/	
	function get_lcs($s1, $s2) {
		//ok, now replace all spaces with nothing
		$s1 = strtolower($this->str_lcsfix($s1));
		$s2 = strtolower($this->str_lcsfix($s2));
		
		$lcs = $this->LCS_Length($s1,$s2); //longest common sub sequence
		
		$ms = (strlen($s1) + strlen($s2)) / 2;
		
		return (($lcs*100)/$ms);
	}
	
    /**
     * searchs the needle in the haystack and returns the position
     * @access private
     * @param string $haystack text where you want to find the needle text
     * @param string $needle text you want to find in the haystack
     * @param boolean $ignoreCase ignore case-sensitivity true or false
     * @return integer $needlePos position of the found position
     */
	function str_contains($haystack, $needle, $ignoreCase = false) {
		if ($ignoreCase) {
			$haystack = strtolower($haystack);
			$needle  = strtolower($needle);
		}
		$needlePos = strpos($haystack, $needle);
		return ($needlePos === false ? false : ($needlePos+1));
	}
	
    /**
     * cut the text in whole words between minimum from start and stop
     * @access private
     * @param string $text text to clip
     * @param integer $start start of clipping
     * @param integer $stop stop of clipping
     * @return string $text clipped text
     */
	function strClippingWholeWords($text, $start, $stop, $delimiter="...") {
		$length = strlen($text);
		
		// suche Wortanfang
		$i = $start;
		while (!ereg("( |:)", substr($text, $i, 1)) AND $i>=0) {
			$i--;
		}
		$start = $i+1;
		
		// suche Wortende
		$i = $stop;
		while (!ereg("( |:|,|\.|\n)", substr($text, $i, 1)) AND $i<$length) {
			$i++;
		}
		$stop = $i;
		
		//echo "Start und Stop: ".$start." ".$stop."<br>";
		
		$text = substr($text, $start, $stop-$start);
		if ($start > 0) $text = $delimiter.$text;
		if ($stop < $length) $text .= $delimiter;
		return $text;
	}
	
	/**
	* Perform a simple text replace
	* This should be used when the string does not contain HTML
	* (off by default)
	*/
	var $STR_HIGHLIGHT_SIMPLE = 1;
	
	/**
	* Only match whole words in the string
	* (off by default)
	*/
	var $STR_HIGHLIGHT_WHOLEWD = 2;
	
	/**
	* Case sensitive matching
	* (on by default)
	*/
	var $STR_HIGHLIGHT_CASESENS = 4;
	
	/**
	* Don't match text within link tags
	* This should be used when the replacement string is a link
	* (off by default)
	*/
	var $STR_HIGHLIGHT_SKIPLINKS = 8;
	
	/**
	* Highlight a string in text without corrupting HTML tags (http://aidan.dotgeek.org/lib/?file=function.str_highlight.php)
	*
    * @access private
	* @author      Aidan Lister <hide@address.com>
	* @version     3.0.0
	* @param       string          $text           Haystack - The text to search
	* @param       array|string    $needle         Needle - The string to highlight
	* @param       bool            $options        Bitwise set of options
	* @param       array           $highlight      Replacement string
	* @return      Text with needle highlighted
	*/
	function str_highlight ($text, $needle, $options = null, $highlight = null) {
		// Default highlighting
		if ($highlight === null) {
			$highlight = '<strong>\1</strong>';
		}
		
		// Select pattern to use
		if ($options & $this->STR_HIGHLIGHT_SIMPLE) {
			$pattern = '#(%s)#';
		} elseif ($options & $this->STR_HIGHLIGHT_SKIPLINKS) {
			// This is not working yet
			$pattern = '#(?!<.*?)(%s)(?![^<>]*?>)#';
		} else {
			$pattern = '#(?!<.*?)(%s)(?![^<>]*?>)#';
		}
		
		// Case sensitivity
		if ($options ^ $this->STR_HIGHLIGHT_CASESENS) {
			$pattern .= 'i';
		}
		
		$needle = (array) $needle;
		foreach ($needle as $needle_s) {
			$needle_s = preg_quote($needle_s);
			
			// Escape needle with optional whole word check
			if ($options & $this->STR_HIGHLIGHT_WHOLEWD) {
				$needle_s = '\b' . $needle_s . '\b';
			}
			
			$regex = sprintf($pattern, $needle_s);
			$text = preg_replace($regex, $highlight, $text);
		}
		
		return $text;
	}
	
	/**
	* sets the starttime for the internal runtime counter
	* @access protected
	*/
	function startTimer() { 
	    $microtime=explode(" ", microtime()); 
    	$this->scriptStartTime=$microtime[1]+$microtime[0]; 
    }  // end func startTimer
	
	/**
	* sets the stoptime for the internal runtime counter
	* @access protected
	* @param int $decimals presicion of the returned runtime
	* @return real $runtime runtime of the script between start and stop of the timer.
	*/
	function stopTimer($decimals = 5) { 
	    $microtime=explode(" ", microtime()); 
    	$this->scriptStopTime =$microtime[1]+$microtime[0]; 
	    return number_format(($this->scriptStopTime -$this->scriptStartTime),$decimals); 
    }// end func stopTimer
}
?>
Return current item: TextIndexer