Templates: Verschachtelte Blöcke

mAu

Erfahrenes Mitglied
Hallo Community.

Da in meiner letzen Frage von CIX88 die Verschachtelung von Blöcken beim Parsen von Templates angesprochen wurde, wollte ich mal fragen, wie das eigentlich funktioniert. Also ich hab heute schon nachgedacht, hab aber keinen Ansatz bekommen, wie man das zustande bringt. Zur Zeit sieht meine Template Klasse so aus, dass ich z.B. von der Datenbank ein Array bekomme, dass etwa so aussieht:
Code:
Array
(
    [0] => Array
        (
            [name] => mAu
            [email] => email@email.de
            [text] => Text...
        )

    [1] => Array
        (
            [name] => mAx
            [email] => email@email.de
            [text] => Noch mehr Text...
        )

    [2] => Array
        (
            [name] => totti
            [email] => email@email.de
            [text] => Und noch mehr...
        )

)

Das übergebe ich dann an eine Methode replaceBlock, die wie folgt aussieht:
PHP:
	private function replaceBlock($bName, $array){
	  
	  preg_match('/<!--Begin:'.$bName.'-->(.+)<!--End:'.$bName.'-->/siU', $this->data, $block);
	  
	  	$tmp = '';
		foreach($array as $tmpArray){
	        
			$obj = new Parser($block[1], true);
		  
			$obj->setArray($tmpArray); 
		  
			$tmp .= $obj->parse(true);
		  	
		}
			
		$this->data = preg_replace('/<!--Begin:'.$bName.'-->(.+)<!--End:'.$bName.'-->/siU', $tmp, $this->data);			
		  
	}

Wie läuft das dann ab, wenn man verschachtelte Blöcke hat? Steh da gerade auf'm Schlauch :)

MfG,
mAu
 
Ich habe das bei meinem TemplateParser mit preg_replace() und einer rekursiven Funktion gelöst. Funktioniert wunderbar. Wenn du den Code willst, schreib ich ihn hier mal her. Es ist aber nicht der Beste Code. Manche Funktionen können weg, oder müssen noch verbessert werden.
 
Das wär nett, dann könnte ich mich erstmal damit beschäftigen und mich "einarbeiten".
*edit*: Wenn du ein kleines Beispiel beilegen könntest, wär toll, muss aber nicht sein!
 
Zuletzt bearbeitet:
Hi, ich habe ein paar Kommentare hinzugefügt. Vll helfen sie ja ein bisschen.
Viel Spaß/Glück bei rumtüffteln ;)

PHP:
<?php
error_reporting(E_ALL); # kann wieder raus. Habs zu Testzwecken rein
class TemplateParser
{
	var $TemplateFiles = array();
	var $TemplatePlacer = array();
	var $CountTemplateFile = -1;
	var $openedTemplate = array();
	var $TemplateFileCodes = array();
	
	/*
	Funktion um eine neue Datei (Template) zu laden
	*/
	function new_template_file($filename)
	{
		$this->CountTemplateFile++;
		$this->TemplateFiles[$this->CountTemplateFile] = $filename;
		$this->TemplatePlacer[$this->CountTemplateFile] = array('.' => array());
		
		$this->openedTemplate[] = implode('',file("C:/server/htdocs/schule/admin/".$filename));
		
		$this->getTemplates($this->CountTemplateFile);

		return true;
	}
	
	/*
	Funktion um ein Fehlertemplate mit Meldung auszugeben
	######## noch nicht ausgereift #########
	*/
	function throwError($msg)
	{
		foreach($this as $key => $value)
		{
			if(is_array($this->$key))
			{
				$this->$key = array();
			}
			else
			{
				$this->$key = -1;
			}
		}

		$this->new_template_file("templates/error.htm");
		$this->add_placer("", "MSG", $msg);
		$this->parseTemplate();
	}
	
	/*
	Funktion um einen einzigen Placer mit Wert zu bestimmen
	*/
	function add_placer($rowPlacer, $rowContent)
	{
		$this->TemplatePlacer[$this->CountTemplateFile]["."][$rowPlacer] = $rowContent;
	}
	
	/*
	Funktion um ein ganzes Array mit Placern einzuspeisen
	*/
	function add_normal_placer_array($array)
	{
		foreach($array as $key => $value)
		{
			$this->TemplatePlacer[$this->CountTemplateFile]["."][$key] = $value;
		}
	}

	/*
	Funktion um Placer für verschachtelte und auch einfache Blöcke zu deklarieren
	*/
	function add_block_placer_array($blockname, $array)
	{
			if (strstr($blockname, '.'))
			{
				$blocks = explode('.', $blockname);
				$blockcount = sizeof($blocks) - 1;
				$str = '$this->TemplatePlacer[$this->CountTemplateFile]';
				for ($i = 0; $i < $blockcount; $i++)
				{
					$str .= '[\'' . $blocks[$i] . '.\']';
					eval('$lastiteration = sizeof(' . $str . ') - 1;');
					$str .= '[' . $lastiteration . ']';
				}
				$str .= '[\'' . $blocks[$blockcount] . '.\'][] = $array;';

				eval($str);
			}
			else
			{
				$this->TemplatePlacer[$this->CountTemplateFile][$blockname . '.'][] = $array;
			}

			return true;
	}
	
	/*
	Funktion um alle Werte der Klasse zurückzuliefern
	*/
	function print_out()
	{
		echo nl2br(print_r($this,false));
	}
	
	/*
	Rekurisve Funktion um die Blöcke zu ersetzten
	####### kann evt verbessert werden #########
	*/
	function blockReplace($blockname, $blockcode, $fileNum)
	{
		$blockcode_copy = $blockcode;
		$data = "";

		if(substr(stripslashes($blockname),0,2) == "a:")
		{
			$blockNum = unserialize(stripslashes($blockname));
		}
		elseif(array_key_exists($blockname.(strstr($blockname,'.')?'':'.'),$this->TemplatePlacer[$fileNum]))
		{
			$blockNum = $this->TemplatePlacer[$fileNum][$blockname.(strstr($blockname,'.')?'':'.')];
		}
		else
		{
			$blockNum = 'NONE';
		}

		
		if($blockNum != 'NONE')
		{

			for($i=0;$i<count($blockNum);$i++)
			{
				$blockcode = $blockcode_copy;
				foreach($blockNum[$i] as $key => $value)
				{
					if(is_array($value) && strstr($key,'.'))
					{
						$key = substr($key,0,-1);
						$dimensions = addslashes(serialize($blockNum[$i][$key.(strstr($key,'.')?'':'.')]));

						$blockcode = preg_replace("#<!-- BEGIN ".$key." -->(.*)<!-- END ".$key." -->#Ueis","\$this->blockReplace('$dimensions', '$1', $fileNum)",$blockcode);
						continue;
					}
					else
					{
						$blockcode = preg_replace("#{[[:space:]]*?(".$key."){1,}?[[:space:]]*?}#", $value, $blockcode);
					}
				}
				$data .= $blockcode;

			}
			return $data;
		}
		else
		{
			return /*$blockcode*/'';
		}
	}
	/*
	Funktion einzelne Templateteile zu filtern
	######## hier nichtmehr im einsatz ########
	*/
	function getTemplates($fileNum)
	{
		if(preg_match_all("#<!-- BEGIN TEMPLATE (.*) -->(.*)<!-- END TEMPLATE \\1 -->#Uis",$this->openedTemplate[$fileNum],$HTMLcode))
		{
			for($i=0;$i<count($HTMLcode[0]);$i++)
   			{
				$this->TemplateFileCodes[$HTMLcode[1][$i]] = $HTMLcode[2][$i];
				preg_replace("#<!-- BEGIN TEMPLATE ".$HTMLcode[1][$i]." -->(.*)<!-- END TEMPLATE ".$HTMLcode[1][$i]." -->#Uis","",$this->openedTemplate[$fileNum]);
   			}
		}
	}

	/*
	Diese Funktion verarbeitet nun alles was gegeben wurde.
	*/
	function parseTemplate()
	{
		for($tmpFile=0;$tmpFile<count($this->openedTemplate);$tmpFile++)
		{
			$this->openedTemplate[$tmpFile] = preg_replace("#<!-- BEGIN (.*) -->(.*)<!-- END \\1 -->#Ueis","\$this->blockReplace('$1','$2', $tmpFile)",$this->openedTemplate[$tmpFile]);
			$i = 0;
			foreach($this->TemplatePlacer[$tmpFile]['.'] as $key => $value)
			{
				$this->openedTemplate[$tmpFile] = preg_replace("#{[[:space:]]*?(".$key."){1,}?[[:space:]]*?}#", $value, $this->openedTemplate[$tmpFile]);
			}
		}
		
		$this->outputParsedPage();
		exit;
	}
	
	/*
	Funktion um einzelne Placer zu ersetzten
	######## hier nichtmehr im einsatz ########
	*/
	function replaceInTemplate($key, $value, $template)
	{
		return preg_replace("#{[[:space:]]*?(".$key."){1,}?[[:space:]]*?}#", $value, $template);
	}
	
	/*
	Gibt die fertigen Templates als Seite aus.
	*/
	function outputParsedPage()
	{
		$code = implode("",$this->openedTemplate);
		$code = preg_replace("#(\\\){1,}(\"|')#Uis","$2",$code);
		echo $code;
	}
}


$tpl = new TemplateParser();
$tpl->new_template_file("pfad/zur/Datei.ext");
$tpl->add_placer("ERROR","Ein Test") # Einen einzelnen Placer setzten
$tpl->add_normal_placer_array(array("DATUM" => date("d.m.y"),  #Ein Array mit PLacern sezten
									"ZEIT" => date("H:i:s"))); #
$tpl->add_block_placer_array("UNLOCK", array("CLASS" => "alt1",       #
											 "AUTHORNAME" => "fanste",#EInen Block hinzufügen
											 ...));                   #

# Gäbe es jetzt noch in dem Block UNLOCK den Block MEHR, wird das so gemacht. usw mit allen weiteren Blocks die in einem Block dirn stecken
$tpl->add_block_placer_array("UNLOCK.MEHR", array("CLASS1" => "alt1",       #
											 "AUTHORNAME1" => "fanste",     #EInen Block hinzufügen
											 ...));         				#

$tpl->throwError("MSG"); # Hiermit kann man einen Fehler auswerfen alssen. Template wird in der Funktion angegeben.
$tpl->print_out(); #Hiermit kannst du dir alles ausgeben lassen, was du zur Klasse bereits hinzugefügt hast

#Du kannst jetzt noch weitere Templates mit Placern hinzufügen, ohne, dass du die Klasse erst inizialisieren musst
#$tpl->new_template_file(FILE);
#$tpl->add_placer(...);

$tpl->parseTemplate(); #parst das/die Template(s)
?>
Ändere noch die Pfade in der Klasse

So könnte so ein Template ausehen.
HTML:
<div align="center">
  {ERROR}<br />
  Datum: {DATUM} &nbsp;Uhrzeit: {ZEIT}
  <table class="tborder" cellpadding="3" cellspacing="0" width="580">
    <tr>
		<td align="left" class="tcat" colspan="2"><b><u>Einträge freischalten</u></b></td>
    </tr>
    <tr>
      <td align="left" class="thead"><span><b>Autor</b></span></td>
      <td align="left" class="thead"><span><b>Nachricht</b></span></td>
    </tr>
    <!-- BEGIN UNLOCK -->
    <tr>
  		<td align="left" valign="top" width="150" class="{CLASS}">
		    <table width="100%" border="0" cellpadding="2" cellspacing="0">
		      <tr>
		        <td align="left" colspan="3"><span class="normal"><b>{AUTHORNAME}</b><br /></span></td>
		      </tr>
		      <tr>
		        <td align="left" colspan="3"><span class="small">vom: {POSTTIME}<br /></span></td>
		      </tr>
		      <tr>
		        <td align="left">{EMAIL}</td>
		        <td align="left">{HOMEPAGE}</td>
		        <td align="left" width="100">{ICQ}</td>
		      </tr>
		    </table>
		</td>
		<td align="left" valign="top" class="{CLASS}">
		    <span class="normal">{MSG}<br /><br /></span>
		    <form action="{URL}" method="post">
		      <input type="hidden" name="postid" value="{POSTID}" />
		      <input type="submit" value="Freischalten" class="button" />&nbsp;
		    </form>
		</td>
	</tr>
		<!-- END UNLOCK -->
	<tr>
      <td colspan="2" align="center" class="tfoot"><span class="normal">&nbsp;</span></td>
    </tr>
  </table>
  <br />
</div>
 
Eine Frage dem Thema etwas abweichend... gibt es eine Möglichkeit aus einem String like Block1.uBlock1.suBlock1.vsuBlock1 ein mehrdimensionales Array zu machen,
etwa so:
Code:
Array
(
    [Block1] => Array
        (
            [uBlock1] => Array
                (
                    [suBlock1] => Array
                        (
                            [vsuBlock1] => ...
                        )

                )

        )

)
ohne eval() zu benutzen? Hab bis jetzt nur Code unter Verwendung von eval gefunden (siehe hier).

MfG mAu
 
Warum, was hast du gegen eval()? Ich habe da auch schon nach einer Möglichkeit gesucht, aber nichts gefunden. Hatte auch hier im Forum gefragt.
 
Ich hoere in letzter Zeit immer oefter Sachen wie:
eval is evil, etc.
daher wollte ch nach einer Moeglichkeit suchen, dass ohne eval zu machen. Hab grad was im Kopf, hoffentlich vergess ich es nicht, sitz grad in der Schule :)
 
Das "eval ist evil" gilt ja nur, wenn du irgendeinen Code ausführst. Wobei in deinem Template ja keine unerwünschten PHP-Codes vorkommen sollten, oder? Mit dem Templateparser kannst du ja auch einfach PHP in dem Template zuerst entfernen. Auch die meisten Templateklassen setzen auf eval().
 
Die einzigste Möglichkeit, die ich mir jetzt vorstellen könnt, wie man dort eval() loswird, ist eine rekursive Funktion.
 
Zurück