[b]voorbeeld.xml[/b]
[code]
<?xml version="1.0"?>

<voorbeeld>
		
	<item>Text</item>
	<![CDATA[
		<a href="text">Testing href</a>
	
	]]>
	<item>Text</item>
	<!-- Comment -->
</voorbeeld>
[/code]

[b]voorbeeld.php[/b]
[code]
<?
include('STATIC_STRING.class.php');
$data = file_get_contents('voorbeeld.xml');

header("Content-type: text/html");

echo "Originele data:<br />";
echo $data;
echo "<br /><hr /><br />";

// Converteer cdata
$data = STATIC_STRING::convert_by_tag($data, '<![CDATA[', ']]>', 'HTMLENTITIES', true);

echo "Geconverteerd cdata:<br />";
echo $data;
echo "<br /><hr /><br />";


// Verwijder comment
$data = STATIC_STRING::convert_by_tag($data, '<!--', '-->', 'STRIP', true);

echo "Zonder commentaar:<br />";
echo $data;
echo "<br /><hr /><br />";

// Verkrijg tags
$arrMatch = array();
STATIC_STRING::match_by_tag($data, '<', '>', $arrMatch);

echo "Gevonden elementen:<br />";
foreach($arrMatch as $positionData)
{
	// Zonder open tag, dus positie + 1, index 3 is binnenlengte
	echo substr($data, $positionData[0]+1, $positionData[3])."<br />";
}
echo "<br /><hr /><br />";


echo "voorbeeld.php compressie:<br />";

$data = file_get_contents('voorbeeld.php');
echo "Huidige lengte: ".strlen($data)."<br />";

// Compact phpcode
$data = STATIC_STRING::compact_php($data);

echo "Compressie lengte: ".strlen($data)."<br /><hr/>";
echo "Compressie code:<br />".htmlentities($data);
echo "<br /><hr /><br />";

echo "STATIC_STRING.class.php compressie:<br />";

$data = file_get_contents('STATIC_STRING.class.php');
echo "Huidige lengte: ".strlen($data)."<br />";

// Compact phpcode
$data = STATIC_STRING::compact_php($data);

echo "Compressie lengte: ".strlen($data)."<br /><hr/>";
echo "Compressie code:<br />".htmlentities($data);
echo "<br /><hr /><br />";
?>
[/code]


[b]STATIC_STRING.class.php[/b]
[code]
<?

class STATIC_STRING
{
	/**
	 * Returns the start and endpostion of each matched set. Array(Array(iStartpos, iEndPos, $iTotalLength, $iInternalLength), Array(iStartpos, iEndpos, $iTotalLength, $iInternalLength))
	 *
	 * @param string $strText
	 * @param string $strTagOpen
	 * @param string $strTagClose
	 * @param array $arrMatch
	 */
	function match_by_tag($strText, $strTagOpen, $strTagClose, &$arrMatch, $iOffset = 0, $iLimit = null)
	{
		$iCount = 0;
		$iStartPos = 0;
		$iCurrentPos = $iOffset;

		// Determine the length of both tags
		$iTagOpenLength = strlen($strTagOpen);
		$iTagCloseLength = strlen($strTagClose);

		// Walk through the text, using the substr function, to find the first occurance
		// Save the first occurance position in the iStartpos
		// If the currentposition is outside the textrange, the loop will quit
		while ($iCurrentPos < strlen($strText)
				&& ($iStartPos = strpos($strText, $strTagOpen, $iCurrentPos)) !== false
				&& ($iLimit == null || $iCount < $iLimit) )
		{
			// Update the current pos, set it to the startposition plus the length of the startstring
		    $iCurrentPos = $iStartPos + $iTagOpenLength;

		    // Try to match the endstring, if not successful, match it to the end of the text
		    $iEndPos = strpos($strText, $strTagClose, $iCurrentPos);
		    if ($iEndPos === false) $iEndPos = strlen($strText);

		    // Update the current position
		    $iCurrentPos = $iEndPos + $iTagCloseLength;

		    $arrMatch[] = array($iStartPos, $iCurrentPos, 
		    				$iCurrentPos - $iStartPos, // Total length
		    				$iCurrentPos - $iStartPos - $iTagOpenLength - $iTagCloseLength); // Internal length
		    $iCount++;
		}
	}

	/**
	 * Convert a string matched by an open and close tag.
	 *
	 * @param string $strText
	 * @param string $strTagOpen
	 * @param string $strTagClose
	 * @param string $strAction = Strip, HTMLENTITIES, HTML_ENTITY_DECODE
	 * @return string
	 */
	function convert_by_tag($strText, $strTagOpen, $strTagClose, $strAction, $blnRemoveTag = true, $iOffset = 0, $iLimit = null)
	{
		$iTagOpenLength = strlen($strTagOpen);
		$iTagCloseLength = strlen($strTagClose);

		$strConverted = "";

		// Use the STATIC_STRING::match_by_tag function
		$arrMatches = array();
		STATIC_STRING::match_by_tag($strText, $strTagOpen,  $strTagClose, $arrMatches, $iOffset, $iLimit);

		// Check if there where any matches, else return the input text
		if(count($arrMatches) > 0)
		{
			// Use the current pos variable to cache the current position in the string
			$iCurrentPos = 0;

			// Walk through the matches
			foreach($arrMatches as $arrPositions)
			{
				if($blnRemoveTag)
				{
					// Without the tags
					//  the current position till the occurance of the open string (length = openstringpos - currentpos)
					//   the opentag is thus removed
					$strConverted .= substr($strText, $iCurrentPos, $arrPositions[0] - $iCurrentPos);
				}
				elseif($iCurrentPos == 0)
				{
					// Do nothing special
					$strConverted .= substr($strText, 0, $arrPositions[0] + $iTagOpenLength);
				}
				else
				{
					// Go back the length of the closing char(cannot when iCurrentpos = 0), to keep it in the converted string
					// The length is the openstringpos - currentpos + openstringlength + closestringlength
					$strConverted .= substr($strText, 
											$iCurrentPos - $iTagCloseLength, 
											$arrPositions[0] - $iCurrentPos + $iTagCloseLength + $iTagOpenLength);
				}

				$strMatch = substr($strText, $arrPositions[0] + $iTagOpenLength, $arrPositions[3]);

				switch(strtoupper($strAction))
				{
					case 'STRIP':
						break;
					case 'HTMLENTITIES':
						$strConverted .= htmlentities($strMatch);
						break;
					case 'HTML_ENTITY_DECODE':
						$strConverted .= html_entity_decode($strMatch);
						break;
				}

				// The currentpos will be updated to the position after the closetag
				// thus: startpos + length
				$iCurrentPos = $arrPositions[0] + $arrPositions[2];
			}

			// Put the text behind
			if($blnRemoveTag)
			{
				$strConverted .= substr($strText, $iCurrentPos);
			}
			else  
			{
				$strConverted .= substr($strText, $iCurrentPos - $iTagCloseLength);
			}
		}
		else {
			$strConverted = $strText;
		}

		return $strConverted;
	}

	/**
	 * Strip a match out of string. Save the match in a referenced array and return the stripped text
	 * The unique tags can be replaced again by InsertMatchedTagsFromArray
	 *
	 * @param string $strText
	 * @param string reference $arrData
	 * @param string $strTagOpen
	 * @param string $strTagClose
	 * @return strText stripped of the tags, replaced with unique tags.
	 */
	function strip_out_and_save_by_tag($strText, &$arrData, $strTagOpen, $strTagClose, $iOffset = 0, $iLimit = null)
	{
		$arrMatch = array();
		
		STATIC_STRING::match_by_tag($strText, $strTagOpen, $strTagClose, $arrMatch, $iOffset, $iLimit);
		
		if($arrMatch > 0)
		{
			// Save the new string in here
			$iPos = 0;
			$strStrippedText = "";
			
			foreach($arrMatch as $stringPos)
			{
				// Join the new output and the oldstring, to generate an absolute unique tag
				$strUniqueTag = STATIC_STRING::generate_unique_tag($strText, $arrData);
				
				// Save the data outside the tags
				$strStrippedText .= substr($strText, $iPos, $stringPos[0] - $iPos);
				
				// Add the unique tag, this is at the location of the match
				$strStrippedText .= $strUniqueTag;
				// Save the match into the array
				$arrData[$strUniqueTag] = substr($strText, $stringPos[0], $stringPos[2]);
				
				// Set the position at the end of the match
				$iPos = $stringPos[1];
			}
			// After all, match the text after the last tag
			$strStrippedText .= substr($strText, $iPos);
			
			$strText = $strStrippedText;
		}
		
		return $strText;
	}
	
	/**
	 * Replace matched tags by their values. Undo's the strip_out_and_save_by_tag function
	 *
	 * @param string $strText
	 * @param array $arrMatchedTags
	 * @return strText
	 */
	function insert_saved_by_tag($strText, $arrMatchedTags)
	{
		foreach($arrMatchedTags as $key=>$value)
		{
			$strText = str_replace($key, $value, $strText);
		}
		
		return $strText;
	}
	
	/**
	 * Get the first position of a set of strings.
	 *
	 * @param string $strText
	 * @param mixeld $arrSearch
	 * @param int $iOffset
	 * @return array($iPosition, $strNeedle)
	 */
	function first_position($strText, $arrSearch, $iOffset = null)
	{
		$iPosition 	= false;
		$strNeedle	= '';
		
		// Convert a single node into the array.
		if(!is_array($arrSearch)) $arrSearch = array($arrSearch);
		
		foreach($arrSearch as $Needle)
		{
			$iTMP = strpos($strText, $Needle, $iOffset);
			if($iTMP !== false && ( $iTMP < $iPosition || $iPosition === false))
			{
				$iPosition 	= $iTMP;
				$strNeedle	= $Needle;
			}
		}

		return array($iPosition, $strNeedle);
	}
	
	/**
	 * Get the last position of a set of strings.
	 *
	 * @param string $strText
	 * @param mixeld $arrSearch
	 * @return array($iPosition, $strNeedle)
	 */
	function last_position($strText, $arrSearch)
	{
		$iPosition 	= false;
		$strNeedle	= '';
		
		// Convert a single node into the array.
		if(!is_array($arrSearch)) $arrSearch = array($arrSearch);
		
		foreach($arrSearch as $Needle)
		{
			$iTMP = strpos($strText, $Needle);
			if($iTMP !== false && ( $iTMP > $iPosition || $iPosition == false))
			{
				$iPosition 	= $iTMP;
				$strNeedle 	= $Needle;
			}
		}

		return array($iPosition, $strNeedle);
	}
	
	/**
	 * This function strips php code, it removes comments, tabs and newlines. It leaves string intact, so newlines in your strings won't be replaced.
	 *
	 * @param string $strText
	 * @return string stripped from comments
	 */
	function compact_php($strText)
	{
		// Don't do anything when a file already contains some of those tags. It would messup the whole thing
		/// Do that by finding the first postion of one of the tags. If it returns false, it doesn't contain any of them
		$strCompressString = '<? /** Compressed with PHPCodeCompacter **/ ?>';
		$iPrefPos = strpos($strText, $strCompressString);
		if($strText != '' &&  ($iPrefPos > 0 || $iPrefPos === false))
		{
			$arr = array();
			$arr['ESCESC'] 	= STATIC_STRING::generate_unique_tag($strText, $arr);
			$arr['ESCSNG'] 	= STATIC_STRING::generate_unique_tag($strText, $arr);
			$arr['ESCDBL'] 	= STATIC_STRING::generate_unique_tag($strText, $arr);
			$arr['SNG'] 	= STATIC_STRING::generate_unique_tag($strText, $arr);
			$arr['DBL'] 	= STATIC_STRING::generate_unique_tag($strText, $arr);
			
			
			// Replace escaped escapes
			$strText = str_replace('\\\\', $arr['ESCESC'], $strText);
			// Replace escaped quotes
			$strText = str_replace(array('\\\'', '\\"'), array($arr['ESCSNG'], $arr['ESCDBL']), $strText);
			
			$arrQuoted = array();
			
			// It is difficult to strip all out in the right way
			//  the most precise manner is by walking through the text and see what comes first
			//  there is comment, what has to be stripped and strings which have to be remove and store
			do 
			{
				$arrFirstCharData = STATIC_STRING::first_position($strText, array('//', '/*', '#', "'", '"'));
				$iStrPos 	= $arrFirstCharData[0];
				$strNeedle 	= $arrFirstCharData[1]; 
				
				if($iStrPos !== false && $strNeedle == '//')
				{
					// It is comment, so strip out comment first
					$strText = STATIC_STRING::convert_by_tag($strText, '//', "\n", 'strip', true, 0, 1);
				}elseif($iStrPos !== false && $strNeedle == '/*')
				{
					// It is comment, so strip out comment first
					$strText = STATIC_STRING::convert_by_tag($strText, '/*', "*/", 'strip', true, 0, 1 );
				}
				elseif($iStrPos !== false && $strNeedle == '"')
				{
					// It isn't comment, so it should be a tag for a single or double quote
					//  Replace the damn quotes!
					$strText = substr_replace($strText, $arr['DBL'], $iStrPos, 1);
					
					// Determine the position of the closing quote after the replace
					$iNextQuote = strpos($strText, '"');
					$strText = substr_replace($strText, $arr['DBL'], $iNextQuote, 1);
					$strText = STATIC_STRING::strip_out_and_save_by_tag($strText, $arrQuoted, $arr['DBL'], $arr['DBL'], 0, 1);
				}elseif($iStrPos !== false && $strNeedle == "'")
				{
					$strText = substr_replace($strText, $arr['SNG'], $iStrPos, 1);
					
					// Determine the position of the closing quote after the replace
					$iNextQuote = strpos($strText, "'");
					$strText = substr_replace($strText, $arr['SNG'], $iNextQuote, 1);
					$strText = STATIC_STRING::strip_out_and_save_by_tag($strText, $arrQuoted, $arr['SNG'], $arr['SNG'], 0, 1);
					echo '';
				}elseif($iStrPos !== false && $strNeedle == '#')
				{
					// It is comment, so strip out comment first
					$strText = STATIC_STRING::convert_by_tag($strText, '#', "\n", 'strip', true, 0, 1);
				}
				
			}while($iStrPos);
			
			// The core removal
			
			// remove whitespace
			$strText = preg_replace('/\s\s+/', ' ', $strText);
			
			$strText = str_replace(" class ", "\r\nclass ", $strText);
			$strText = STATIC_STRING::insert_saved_by_tag($strText, $arrQuoted);
			
			// Undo the escaping in shifted order
			$strText = str_replace(array($arr['SNG'], $arr['DBL']), array('\'', '"'), $strText);
			$strText = str_replace(array($arr['ESCSNG'], $arr['ESCDBL']), array('\\\'', '\\"'), $strText);
			$strText = str_replace($arr['ESCESC'], '\\\\', $strText);
			
			
			$strText = str_replace('?><?', '', $strCompressString.$strText);
			
			return $strText;
		}
		
		return trim($strText);
	}
	
	/**
	 * This function strips php code, it removes comments, tabs and newlines. It leaves strings intact, so newlines in your strings won't be replaced.
	 *
	 * @param string $strText
	 * @return string stripped from comments
	 */
	function compact_xml($strText)
	{
		// Don't do anything when a file already contains some of those tags. It would messup the whole thing
		/// Do that by finding the first postion of one of the tags. If it returns false, it doesn't contain any of them
		$strCompressString = '<!-- /** Compressed with PHPCodeCompacter **/ -->';
		$iPrefPos = strpos($strText, $strCompressString);
		if($strText != '' &&  ($iPrefPos > 0 || $iPrefPos === false))
		{
			$arr = array();
			$arr['SAFEDAT_OPEN'] = STATIC_STRING::generate_unique_tag($strText, $arr);
			$arr['SAFEDAT_CLOSE'] = STATIC_STRING::generate_unique_tag($strText, $arr);
			$arrSafe = array();
			
			// It is difficult to strip all out in the right way
			//  the most precise manner is by walking through the text and see what comes first
			//  there is comment, what has to be stripped and strings which have to be remove and store
			do 
			{
				$arrFirstPosData = STATIC_STRING::first_position($strText, array('<![CDATA[', '<!--'));
				$iStrPos 	= $arrFirstPosData[0];
				$strNeedle 	= $arrFirstPosData[1];
				
				if($iStrPos && $strNeedle == '<![CDATA[')
				{
					// It is cdata, save the data in the save array
					$strText = substr_replace($strText, $arr['SAFEDAT_OPEN'], $iStrPos, 9);
					
					// Determine the position of the closing quote after the replace
					$iSafedatClose = strpos($strText, ']]>');
					$strText = substr_replace($strText, $arr['SAFEDAT_CLOSE'], $iSafedatClose, 3);
					$strText = STATIC_STRING::strip_out_and_save_by_tag($strText, $arrSafe, $arr['SAFEDAT_OPEN'], $arr['SAFEDAT_CLOSE'], 0, 1);
				}elseif($iStrPos && $strNeedle == '<!--')
				{
					// It is comment, so strip out comment first
					$strText = STATIC_STRING::convert_by_tag($strText, '<!--', "-->", 'strip', true, 0, 1 );
				}
				
			}while($iStrPos);
			
			// The core removal
			$strText = str_replace(array("\r\n", "\n", "\t", ' ', '   ', '    ', '     '), ' ', $strText);
			
			$strText = STATIC_STRING::insert_saved_by_tag($strText, $arrSafe);
			
			// Undo the escaping in shifted order
			$strText = str_replace(array($arr['SAFEDAT_OPEN'], $arr['SAFEDAT_CLOSE']), array('<![CDATA[', ']]>'), $strText);
			
			$strText = $strText.$strCompressString;
		}
		
		return trim($strText);
	}
	
	/**
	 * When you want to generate an unique tag that isn't in a string or in a array
	 *
	 * @param string $TextUnique
	 * @param array $ArrayUnique
	 * @return Unique Tag
	 */
	function generate_unique_tag($TextUnique = null, $ArrayUnique = null)
	{
		$strTag = "";
		$iTagLength = 5;
		do{
			$strTag = '['.substr(md5(rand(0,10000).date("I-Y-sM:y:m")), 0, $iTagLength).']';
			$iTagLength++;
		}while( (isset($TextUnique) && strstr($TextUnique, $strTag))
					|| (isset($ArrayUnique) && (array_search($strTag, $ArrayUnique) || isset($ArrayUnique[$strTag]))));
		
		return $strTag;
	}
}

?>[/code]