Location: PHPKode > scripts > Siviglia Templating > SivigliaTemplates.php
<?php
/*

  Siviglia Framework templating engine

  BSD License

  Copyright (c) 2010, Jose Maria Rodriguez Millan
  All rights reserved.

  Redistribution and use in source and binary forms, with or without modification, are permitted provided that 
  the following conditions are met:

  * Redistributions of source code must retain the above copyright notice, this list of conditions and 
    the following disclaimer.
  * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and 
    the following disclaimer in the documentation and/or other materials provided with the distribution.
  * Neither the name of the <ORGANIZATION> nor the names of its contributors may be used to endorse or 
    promote products derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, 
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
ARE DISCLAIMED. 
IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 
OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.


Please, send bugs, feature requests or comments to: 
hide@address.com


*/
class CTemplateNode {
    var $type;
    var $textContent;
    var $childNodes;
    var $tagName;
    var $closed;
    var $nestLevel;
    var $assignedName;
    var $dotPrepended=0;
    var $template;
    var $startOffset;
    var $sourceTokens=array();
    var $parsed=0;
    var $widgetRoot=0;
    var $shared=0;
    var $srcContext;
    var $destContext;
    function __construct($type,& $template,$startOffset)
    {
        $this->template=$template;
        $this->startOffset=$startOffset;
        $this->type=$type;
        $this->closed=0;
        $this->nestLevel=0;
        $this->childNodes=array();
        $this->assignedName='$__'.uniqid()."__";
    }
    function duplicate($copyChilds=1)
    {
        $oNode=new CTemplateNode($this->type,$this->template,$this->startOffset);
        if($copyChilds)
        {
            $nChilds=count($this->childNodes);
            if($nChilds>0)
            {
                for($k=0;$k<$nChilds;$k++)
                    $oNode->childNodes[]=$this->childNodes[$k]->duplicate();
            }
        }
        $passedVars=array("shared","widgetRoot","parsed","textContent","tagName","closed","nestLevel","assignedName","dotPrepended","startOffset","srcContext","destContext");
        for($k=0;$k<count($passedVars);$k++)
            $oNode->{$passedVars[$k]}=$this->{$passedVars[$k]};
        return $oNode;
    }
    function initialize()
    {
        // En esta funcion, vemos si somos puro texto, un tag de apertura, o uno de cierre.

        if($this->type=="TAG")
        {
            $k=1;
            
            if($this->textContent[$k]==";")
            {
                $this->shared=1;
                $k++;
            }
            for(;$this->textContent[$k]==":";$k++,$this->nestLevel++);
            $this->textContent=trim($this->textContent,"[:;]");
            if($this->textContent[0]=="_")
                $this->type="TAG_OPEN";
            else
                $this->type="TAG_CLOSE";
            $this->tagName=trim($this->textContent,"_/");
            
        }
    }
    function addChar($chr)
    {
        $this->textContent.=$chr;
    }
    function parse_addChild(& $node)
    {
        // Este metodo solo es usado en los nodos de tipo "TAG_CLOSE"
        $this->closed=1;
        if($node->type=="TAG_OPEN" && $node->tagName==$this->tagName && $node->nestLevel==$this->nestLevel)
        {
            // Los nodos se han ido aniadiendo de atras hacia adelante..Por ello,
            // una vez cerrado, le damos la vuelta al array, y ponemos los nodos hijos
            // en el orden correcto.
            $this->childNodes=array_reverse($this->childNodes);
            return 0;
        }
        $this->childNodes[]=& $node;
        return 1;
    }
    function addChild(& $node)
    {
        $this->childNodes[]=$node;
    }
    function length()
    {
        return strlen($this->textContent);
    }
    function adjustOffsets($increment)
    {
        $this->startOffset+=$increment;
        if($this->type=="TEXT")
            return $increment;

        $increment+=$this->increasedOffsetStart;
        for($j=0;$j<count($this->childNodes);$j++)
            $increment=$this->childNodes[$j]->adjustOffsets($increment);
        $increment+=$this->increasedOffsetEnd;
        return $increment;

    }
    function toVarString(& $template)
    {
        //echo "PROCESANDO TOVARSTRING DE ".($this->type=="TEXT"?htmlentities($this->textContent):$this->tagName)."<br>";
        $template->addNodeToVarRelationship($this,$this->assignedName);
        if($this->type=="TEXT")
        {
            //echo "SALGO POR TIPO==TEXT<br>";
            return $this->textContent;
        }

        if(count($this->childNodes)==0)
        {
            //echo "SALGO POR CHILDNODES=0<br>";
            $this->increasedOffsetStart=strlen($this->assignedName)-(2+strlen($this->tagName)+1);
            return $this->assignedName;
        }

        $str="/* ".trim($this->assignedName,'$')." - BEGIN */";
        $this->increasedOffsetStart=strlen($str)-(2+strlen($this->tagName)+1);

        $nChilds=count($this->childNodes);
        $needDot=0;
        for($k=0;$k<$nChilds;$k++)
        {
            if($this->childNodes[$k]->closed==0 && $this->childNodes[$k]->type!="TEXT") // O es un tag suelto, o es un texto
            {
                // Si no es el primero, le ponemos un punto delante.
                if($needsDot)
                {    
                    $str.=".";
                    $this->childNodes[$k]->dotPrepended=1;
                }
                $needsDot=1;
            }
            else
                $needsDot=0;
            $str.=$this->childNodes[$k]->toVarString($template);
        }
        $postFix="/* ".trim($this->assignedName,'$')." - END */";
        $this->increasedOffsetEnd=strlen($postFix)-(2+strlen($this->tagName)+1);
        $str.=$postFix;
        //echo "TOVARSTRING DE ".$this->tagName.":".htmlentities($str)."<br><br>";
        return $str;
    }
    function dump()
    {
        echo '<div style="border:1px solid black;margin:5px"><div style="background-color:green;color:white;border-bottom:1px solid black">'.($this->type=="TEXT"?"NODO TEXTO":$this->type.": <b>".$this->tagName."</b> (".$this->nestLevel.")").'-----[SRC:'.$this->srcContext.',DEST:'.$this->destContext.'] -- '.$this->startOffset.' Parsed:'.$this->parsed.'</div>';
        if($this->type=="TEXT")
            echo "<div>".htmlentities($this->textContent)."</div>";
        else
        {
            echo "<div><b>".$this->tagName.($this->widgetRoot===0?"":" ".$this->widgetRoot)."</b></div>";
        }
        echo "<div style=\"background-color:#DDDDDD\">".htmlentities($this->resolvedText)."</div>";
        if(count($this->childNodes) > 0)
        {
            echo '<div style="margin:5px 10px 5px 20px">';
            for($k=0;$k<count($this->childNodes);$k++)
                $this->childNodes[$k]->dump();
            echo '</div>';
        }
        echo "</div>";
    }
    function getContext()
    {
        $c=$this->template->nodeToVar[trim($this->assignedName,'$')];
        
        if($c=="")
        {
            // Buscamos el contexto mas cercano..
            $keys=& $this->template->offsetKeys;
            $nKeys=count($keys);
            $k=0;
            $len=$this->startOffset+strlen($this->textContent);
            while($keys[$k]<$len && $k<$nKeys)
            {
                $k++;
            }
            $k--;
            if($k<0)
                return "HTML";
            return $this->template->offsetToContext[$keys[$k]];
        }
        return $c;
    }
    function nodeToText()
    {
        if($this->resolvedText)
            return $this->resolvedText;
        if($this->type=="TEXT")
            return $this->prefixText.$this->textContent.$this->postfixText;
        $dblDots="";
        for($k=0;$k<$this->nestLevel;$k++)
            $dblDots.=":";
        if($this->tagName && $this->parsed==0)
            $str="[_".$dblDots.$this->tagName."]";
        $str.=$this->prefixText;

        $nNodes=count($this->childNodes);
        if($this->closed==0 && $nNodes ==0)
            return;
        for($k=0;$k<$nNodes;$k++)
            $str.=$this->childNodes[$k]->nodeToText();
        $str.=$this->postfixText;
        if($this->tagName && $this->parsed==0)
            $str.="[/".$dblDots.$this->tagName."]";
        return $str;
    }
    function fixSyntax()
    {
        
        // 1) Se ejecuta fixSyntax sobre los hijos.
        $nNodes=count($this->childNodes);
        
        for($k=0;$k<$nNodes;$k++)
            $this->childNodes[$k]->fixSyntax();
        // Se examinan todos los hijos de nuevo, viendo nuestra relacion con ellos.
        $nTags=0;
        for($k=0;$k<$nNodes;$k++)
        {
            
            // Si el tipo del hijo es "TEXT"...
            if($this->childNodes[$k]->type=="TEXT")
            {
                // Hay 2 posibilidades: 1) Soy un tag cerrado, o soy un tag abierto.
                // Si soy un tag cerrado, simplemente, copio el contenido.
                // Si soy un tag abierto, tengo que procesar el contenido, y convertirlo a mi contexto.
                /*if(!$this->closed)
                    $this->fixText($this->childNodes[$k]);*/
                $this->resolvedText.=$this->childNodes[$k]->textContent;
            }
            else
            {
                
                $nTags++;
                // El hijo es un tipo TAG.Esto implica que nosotros somos un TAG tipo closed.
                // Si el TAG es el comienzo de otro widget,o un TAG_OPEN tenemos que intentar salir a HTML.
                // Si el TAG es un TAG_CLOSE, hay que, simplemente, aniadir su texto.
                /*if(!$this->childNodes[$k]->closed || $this->childNodes[$k]->widgetRoot)
                    $this->fixText($this->childNodes[$k]);*/
                /*if($this->childNodes[$k]->widgetRoot)
                {
                    echo "detectado<br>";
                    $this->fixText($this->childNodes[$k]);
                    $this->resolvedText.=$this->childNodes[$k]->resolvedText;
                }
                else*/
                    $this->resolvedText.=$this->childNodes[$k]->resolvedText;
                
            }
        }
        // En el caso de que el tag actual sea un [_*],y su contenido sea algo mas que un simple texto,
        // los tags de nivel inferior ya han realizado la conversion necesaria, y no tenemos que repetirla.
        // Por ello se controla que si un tag no cerrado tiene como unico hijo a otro tag no cerrado, no se repite
        // la conversion.
        if(!$this->closed && $this->type!="TEXT")
        {
            if(count($this->childNodes)==1 && $this->childNodes[0]->type=="TEXT")
            {
                //$this->fixText($this);
                // Mirar aqui a que se ha hecho fixtext.
                
                $this->childNodes[0]->fixText($this->childNodes[0]);
                $this->resolvedText=$this->childNodes[0]->resolvedText;
            }
               
            
        }
        if($this->widgetRoot)
        {
            $this->fixText($this);
        }
        else
        {
            $this->resolvedText=($this->type=="TEXT"?$this->textContent:$this->resolvedText);
        }
        if($this->type!="TEXT")
        
        $this->resolvedText=str_replace(array('<?="";?>','<?php echo ""?>','<? ?>','<?php ?>','<??>','<?php; ?>',
                                              '<? echo "";?>','<?php echo ""; ?>','<? ?>','<?php;?>'),
                                        '',
                                        $this->resolvedText);
        //echo "<hr>RESOLVED TEXT PARA ".$this->tagName."<hr>";
        //echo htmlentities($this->resolvedText);
        
    }
    // Convierte el texto pasado, que estaba en el contexto origContext, al
    // contexto especificado en dstContext.
    function fixText(& $node)
    {
        // Se lee: Tengo que convertir a origcontext algo que estaba en dstContext
        $origContext=$this->srcContext;
        $dstContext=$node->destContext;
        //echo "BUSCANDO CONVERSION DE $origContext a $dstContext.Cadena:".htmlentities(($node->type=="TEXT"?$node->textContent:$node->resolvedText))."<br>";
        if($dstContext=="")
            $dstContext="HTML";
        if($origContext == $dstContext)
        {
            if($node->type=="TEXT")
            {
                if(preg_match("/^\\\$[a-zA-Z0-9_\[\]\"'.\(\),]*$/",$node->textContent))
                    $node->resolvedText='<?php echo '.$node->textContent.';?>';
                else
                    $node->resolvedText=$node->textContent;
                return;
            }
        }
        // Conversion "bestia" de html a variable.Si el texto comienza con $,y lo que viene detras
        // son exclusivamente caracteres, cambiamos el contexto.
        $firstChar=$node->textContent[0];
        if($dstContext=="HTML" && $firstChar=='$')
        {
            if(preg_match("/^\\\$[a-zA-Z0-9_\[\]\"'.\(\),]*$/",$node->textContent))
                $dstContext="VARVALUE";      
        }
        if(($dstContext=="VARVALUE_ECHO" || $dstContext=="VARVALUE" || $dstContext=="ARRAY_INDEX" ) && !($firstChar=='$' || $firstChar=='"' || $firstChar=="'"))
            $dstContext="DBLQUOTE";
        $contextConversions=
            array("HTML"=>array(
                // Tengo que convertir a HTML, algo que estaba en PHP
                // Pues lo vuelvo a poner dentro de PHP
              "PHP"=>array("TEXT"=>array("<?php ","?>")),
                // Tengo que convertir a HTML algo que estaba en VARVALUE
              "VARVALUE"=>array("ERR"=>1),
                // Tengo que convertir a HTML algo que estaba en VARVALUE_ECHO
                // Volvemos a poner el echo
              "VARVALUE_ECHO"=>array("TEXT"=>array("<?php echo ","?>")),
                // Tengo que convertir a HTML algo que estaba en DBLQUOTE..Tal cual.
              "DBLQUOTE"=>array(),
              "SNGLQUOTE"=>array(),
                // Tengo que convertir a HTML algo que estaba en echo...Va tal cual.
              "DBLQUOTE_ECHO"=>array(),
              "SNGLQUOTE_ECHO"=>array(),
                // Tengo que convertir a HTML algo que estaba en ARRAY_INDEX..Tal cual.
              "ARRAY_INDEX"=>array()
            ),
              "PHP"=>array(
                  // Tengo que convertir en PHP algo que estaba en HTML.Salgo a HTML
                  "HTML"=>array("TEXT"=>array("?>","<?php")),
                  // Tengo que convertir en PHP algo que estaba en VARVALUE.Esto no esta permitido
                  "VARVALUE"=>array("ERR"=>1),
                  // en PHP, algo que estaba en VARVALUE_ECHO : Salimos de php, hacemos el echo, y continuamos.
                  "VARVALUE_ECHO"=>array("TEXT"=>array(" ?><?php echo ",";?><?php ")),
                  // Tenemos que convertir en PHP algo que estaba en DBLQUOTE.No se permite
                  "DBLQUOTE"=>array("ERR"=>1),
                  "SNGLQUOTE"=>array("ERR"=>1),
                  // Tenemos que convertir en PHP algo que estaba en DBLQUOTE_ECHO.
                  // Hay que hacer el echo, aniadiendo las comillas necesarias.
                  "DBLQUOTE_ECHO"=>array("TEXT"=>array("?><?php echo \"","\";?><?php ")),
                  "SNGLQUOTE_ECHO"=>array("TEXT"=>array("?><?php echo '","';?><?php ")),
                  // Convertir en PHP algo que era el indice de un array?
                  "ARRAY_INDEX"=>array("ERR"=>1)
                  ),
              "VARVALUE"=>array(
                  // Convertir en VARVALUE algo que estaba en HTML..Se convierte a cadena.
                  "HTML"=>array("ACTION"=>"getStr"),
                  // Convertir en VARVALUE algo que era PHP?? No, no vamos a asignar codigo a una
                  // variable.Error.
                  "PHP"=>array("ERR"=>1),
                  // Convertir en VARVALUE algo que estaba en VARVALUE_ECHO...Se asigna,como getStr
                  "VARVALUE_ECHO"=>array("ACTION"=>"getStr"),
                  // Convertir en VARVALUE algo que estaba en DBLQUOTE..Solo hay que obtener la cadena.
                  "DBLQUOTE"=>array("ACTION"=>"getStr"),
                  "SNGLQUOTE"=>array("ACTION"=>"getStr"),
                  "DBLQUOTE_ECHO"=>array("ACTION"=>"getStr"),
                  "SNGLQUOTE_ECHO"=>array("ACTION"=>"getStr"),
                  // Convertir en VARVALUE algo que era indice de un array..Se devuelve lo mismo.
                  "ARRAY_INDEX"=>array("TEXT"=>array("",""))
                  ),
              "VARVALUE_ECHO"=>array(
                  // Convertir en varvalue_echo algo que estaba en html..Se sale del echo.
                  "HTML"=>array("DONTCUT"=>"getStr","TEXT"=>array("\"\";?>","<?php echo \"\"")),
                  // Convertir en varvalue_echo algo que estaba en php..
                  // Se podria salir a php, y que se ejecutara el php.Pero esto rompe la idea
                  // de la template, que es hacer un echo.Por lo tanto, damos error.
                  "PHP"=>array("ERR"=>1),
                  // Convertir en varvalue_echo algo que  era varvalue..No hay que hacer nada.
                  "VARVALUE"=>array("TEXT"=>array("","")),
                  // Convertir en varvalue_echo algo que estaba entre comillas.Solo tengo que obtener una cadena.
                  "DBLQUOTE"=>array("ACTION"=>"getStr"),
                  "SNGLQUOTE"=>array("ACTION"=>"getStr"),
                  "DBLQUOTE_ECHO"=>array("ACTION"=>"getStr"),
                  "SNGLQUOTE_ECHO"=>array("ACTION"=>"getStr"),
                  // Convertir en varvalue_echo algo que era un indice de un array...lo dejamos tal cual.
                  "ARRAY_INDEX"=>array("TEXT"=>array("",""))
                  ),
              "DBLQUOTE"=>array(
                  // Convertir en DBLQUOTE algo que era HTML..Se escapea.
                  "HTML"=>array("ACTION"=>"onQuote"),
                  // Convertir en DBLQUOTE algo que era PHP...No se permite.
                  "PHP"=>array("ERR"=>1),
                  // Convertir en DBLQUOTE algo que era VARVALUE..Salimos de la cadena
                  // y le unimos el valor.
                  "VARVALUE"=>array("TEXT"=>array("\".",".\"")),
                  "VARVALUE_ECHO"=>array("TEXT"=>array("\".",".\"")),
                  // Aqui hay que cambiar el escaping
                  "SNGLQUOTE"=>array("ACTION"=>"onQuote"),
                  // Convertir en DBLQUOTE un DBLQUOTE_ECHO es dejarlo tal cual.
                  "DBLQUOTE_ECHO"=>array(),
                  // convertir en DBLQUOTE un SNGLQUOTE_ECHO es convertir las comillas.
                  "SNGLQUOTE_ECHO"=>array("ACTION"=>"onQuote"),
                  // convertir en DBLQUOTE algo que era un indice de un array...Lo mismo
                  // que si fuera VARVALUE
                  "ARRAY_INDEX"=>array("TEXT"=>array("\".",".\""))
                  ),
              "SNGLQUOTE"=>array(
                  // Convertir en SNGLQUOTE algo que era HTML...Se escapea
                  "HTML"=>array("ACTION"=>"onSnglQuote"),
                  // Convertir en SNGLQUOTE algo que era PHP..No se permite
                  "PHP"=>array("ERR"=>1),
                  // Convertir en SNGLQUOTE algo que era VARVALUE..Se sale de la comilla,
                  // y se introduce el valor.
                  "VARVALUE"=>array("TEXT"=>array("'.",".'")),
                  "VARVALUE_ECHO"=>array("TEXT"=>array("'.",".'")),
                  // Convertir en SNGLQUOTE algo que era DBLQUOTE..Se escapea
                  "DBLQUOTE"=>array("ACTION"=>"onSnglQuote"),
                  "DBLQUOTE_ECHO"=>array("ACTION"=>"onSnglQuote"),
                  "SNGLQUOTE_ECHO"=>array(),
                  // Convertir en SNGLQUOTE algo que era un indice de array..Lo mismo que
                  // VARVALUE
                  "ARRAY_INDEX"=>array("TEXT"=>array("'.",".'"))
                  ),
              "DBLQUOTE_ECHO"=>array(
                  // Convertir en DBLQUOTE_ECHO algo que era HTML..Salimos del echo.
                  "HTML"=>array("DONTCUT"=>"onQuote","TEXT"=>array("\";?>","<?php echo \"")),
                  // Convertir en DBLQUOTE_ECHO algo que era PHP...Podriamos salir de la cadena,
                  // y ejecutar el php, pero esto romperia la idea.Lo que se quiere es hacer echo.
                  "PHP"=>array("ERR"=>1),
                  // Convertir en DBLQUOTE_ECHO algo que era VARVALUE: salimos de las comillas, y pegamos
                  // el varvalue.
                  "VARVALUE"=>array("TEXT"=>array("\".",".\"")),
                  "VARVALUE_ECHO"=>array(),
                  // Convertir en DBLQUOTE_ECHO algo que era DBLQUOTE: Se sale de PHP, y hacemos echo.
                  "DBLQUOTE"=>array("DONTCUT"=>"onQuote","TEXT"=>array("\";?>","<?php echo \"")),
                  // Convertir en DBLQUOTE_ECHO algo que era SNGLQUOTE:se escapea
                  "SNGLQUOTE"=>array("ACTION"=>"onQuote"),
                  "SNGLQUOTE_ECHO"=>array("ACTION"=>"onQuote"),
                  // Convertir en DBLQUOTE_ECHO algo que era ARRAY_INDEX...Muy raro.No se permite
                  // Se deja tal cual
                  "ARRAY_INDEX"=>array("TEXT"=>array("\".",".\""))
                  ),
              "SNGLQUOTE_ECHO"=>array(
                  // Convertir en SNGLQUOTE_ECHO algo que era HTML:Salimos del echo.
                  "HTML"=>array("DONTCUT"=>"onSnglQuote","TEXT"=>array("';?>","<?php echo '")),
                  // Convertir en SNGLQUOTE_ECHO algo que era HTML.No se permite.
                  "PHP"=>array("ERR"=>1),
                  // Convertir en SNGLQUOTE_ECHO algo que era un VARVALUE:
                  "VARVALUE"=>array("TEXT"=>array("'.",".'")),
                  "VARVALUE_ECHO"=>array("TEXT"=>array("'.",".'")),
                  // Convertir en SNGLQUOTE_ECHO algo que era DBLQUOTE:Se escapea.
                  "DBLQUOTE"=>array("ACTION"=>"onSnglQuote"),
                  // Convertir en SNGLQUOTE_ECHO algo que era SNGLQUOTE: Se mantiene
                  "SNGLQUOTE"=>array("TEXT"=>array("","")),
                  "DBLQUOTE_ECHO"=>array("ACTION"=>"onSnglQuote"),
                  // Convertir en SNGLQUOTE_ECHO algo que era un ARRAY_INDEX...Se deja tal cual.
                  "ARRAY_INDEX"=>array("TEXT"=>array("'.",".'"))
                  ),
              "ARRAY_INDEX"=>array(
                  // Convertir en ARRAY_INDEX algo que era HTML..Se obtiene una cadena
                  "HTML"=>array("ACTION"=>"getStr"),
                  // Convertir en ARRAY_INDEX algo que era PHP..No se permite.
                  "PHP"=>array("ERR"=>1),
                  // Convertir en ARRAY_INDEX algo que era un VARVALUE..Se deja tal cual.
                  "VARVALUE"=>array(),
                  // convertir en ARRAY_INDEX algo que era un VARVALUE_ECHO..se deja tal cual.
                  "VARVALUE_ECHO"=>array(),
                  // convertir en ARRAY_INDEX algo que era DBLQUOTE..Se obtiene una cadena.
                  "DBLQUOTE"=>array("ACTION"=>"getStr"),
                  // convertir en ARRAY_INDEX algo que era SNGLQUOTE..Se obtiene una cadena
                  "SNGLQUOTE"=>array("ACTION"=>"getStr"),
                  // convertir en ARRAY_INDEX algo que era DBLQUOTE_ECHO..Se obtiene una cadena
                  "DBLQUOTE_ECHO"=>array("ACTION"=>"getStr"),
                  "SNGLQUOTE_ECHO"=>array("ACTION"=>"getStr"),
                  )
              );
        $err=$contextConversions[$origContext][$dstContext]["ERR"];
        if($err)
        {
             echo "<b>CONVERSION ERROR, $origContext --> $dstContext</b><br>";
             echo "Destination template :".$this->template->fileName." , Source template:".$node->template->fileName."<br>";
             //exit(-1);
             return -1;
        }
        $texts=$contextConversions[$origContext][$dstContext]["TEXT"];
        if($node->type=="TEXT")
                $property="textContent";
            else
                $property="resolvedText";

        if($texts)
        {
            $node->prefixText=$texts[0];
            $node->postfixText=$texts[1];
            if($contextConversions[$origContext][$dstContext]["DONTCUT"] && strlen($node->$property)<20)
            {
                    //echo "<b>CAMBIO ".$node->textContent."</b>";
                    $contextConversions[$origContext][$dstContext]["ACTION"]=
                        $contextConversions[$origContext][$dstContext]["DONTCUT"];
            }
            else
            {
                $node->resolvedText=$node->prefixText.$node->$property.$node->postfixText;
                return;
            }
        }
            if($contextConversions[$origContext][$dstContext]["ACTION"]=="")
                return;
            
            $node->resolvedText=$this->getStr($node->$property);
            switch($contextConversions[$origContext][$dstContext]["ACTION"])
            {
            case "getStr":
                {
                    // Ya hecho por defecto.
                }break;
            case "onQuote":
                {
                    /* On quote tiene un ligero problema, al igual que la funcion getStr.
                       Si el texto era un html puro, el eliminar las comillas funciona.
                       ej:  hola "juanito"   --->   hola \"juanito\" , quedando en contexto : "hola \"juanito\""
                       Sin embargo, si contiene codigo php del tipo:
                       bbb <? echo $c ?> qqq ---> bbb <? echo $c ?> qqq , quedando en contexto : "bbb <? echo $c ?>qqq"
                       lo que provocaria que $c se evaluara en el momento de la asignacion de la cadena.
                        
                        Se obtiene una cadena (para resolver
                        posible php simple contenido en la cadena), y luego se aniaden las comillas
                        Sigue quedando el problema de multiples quotings.
                       */    
                    // Si el texto tiene comillas por delante o detras, se quitan (efecto de getStr)
                    $node->resolvedText=trim($node->resolvedText,"\"' ");
                    $node->resolvedText=str_replace('"',"\\\"",$node->resolvedText);
                    // Se elimina 1 nivel de doble escape:
                    $node->resolvedText=str_replace("\\\\\"","\\\"",$node->resolvedText);
                }break;
            case "onSnglQuote":
                {
                    $node->resolvedText=trim($node->resolvedText,"\"' ");
                    $node->resolvedText=str_replace("'","\\'",$node->resolvedText);
                    // Se elimina 1 nivel de doble escape:
                    $node->resolvedText=str_replace("\\\\'","\\'",$node->resolvedText);
                }break;
                
            }
        
    }
    function getStr($cad)
    {
     // La variable strType nos va a decir que tipo de str hemos encontrado:
    // 0: lo que teniamos era un simple texto (HTML).Debe ser escapeado y entrecomillado.
    // 1: lo que teniamos era un texto, pero empezaba con $
    // 2: lo que teniamos era un php con echo de una expresion.
        //echo htmlentities($cad);
    $tokens=token_get_all($cad);
    $nTokens=count($tokens);
    $subCads=array();
    $currentState=0;
    $k=0;
    $error=0;
    while($k < $nTokens)
    {
        if(is_array($tokens[$k]))
        {
            switch($tokens[$k][0])
            {
            case T_OPEN_TAG:
                    {
                        $k++;
                        while(is_array($tokens[$k]) && $tokens[$k][0]==T_WHITESPACE && $k< $nTokens)
                            $k++;
                        if($k > $nTokens || !is_array($tokens[$k]) || $tokens[$k][0]!=T_ECHO)
                        {
                            return "ERROR : UNEXPECTED TOKEN. EXPECTED TOKEN WAS \"ECHO\"";
                        }
                        // No hago break..Continuamos en el siguiente, si lo que hemos encontrado ha sido
                        // T_ECHO
                    }
                case T_OPEN_TAG_WITH_ECHO:
                    {
                        $k++;
                        $fCad="";
                        while(!(is_array($tokens[$k]) && $tokens[$k][0]==T_CLOSE_TAG) 
                              && !(!is_array($tokens[$k]) && $tokens[$k]==';') && $k < $nTokens)
                        {
                            // Si nos encontramos algo que no sea una cadena fija d
                            if(is_array($tokens[$k]) && $tokens[$k][0]!=T_ENCAPSED_STRING)
                                $needsPHP=1;
                            if(is_array($tokens[$k]))
                                $fCad.=$tokens[$k][1];
                            else
                                $fCad.=$tokens[$k];
                            $k++;
                        }
                        if($tokens[$k]==';')
                        {
                            // Veamos si termina aqui PHP...
                            $k++;
                            while((is_array($tokens[$k]) && 
                                  ($tokens[$k][0]==T_WHITESPACE)) || $tokens[$k]==';')
                                $k++;
                        }
                        if($k == $nTokens)
                        {
                            return "ERROR: PHP EXPRESSION END NOT FOUND<br>";
                        }
                        if(!is_array($tokens[$k]) || (is_array($tokens[$k]) && $tokens[$k][0]!=T_CLOSE_TAG))
                        {
                            return "ERROR:EXPECTING TAG_CLOSE (found ".token_name($tokens[$k][0]);
                        }
                        $subCads[]=$fCad;
                        
                    }break;
            case T_INLINE_HTML:{
                if($tokens[$k][1][0]=='$')
                    $subCads[]=$tokens[$k][1];
                else
                    $subCads[]='"'.str_replace('"',"\\\"",$tokens[$k][1]).'"';
            }break;
            }
        }
        else
        {
            return "ERROR : UNEXPECTED TOKEN :".$tokens[$k]."<br>";
        }
        $k++;
    }
    return implode(".",$subCads);

}
    function resolveContext()
    {
        for($k=0;$k<count($this->childNodes);$k++)
            $this->childNodes[$k]->resolveContext();
        $this->srcContext=$this->getContext();
    }
     
}

class CTemplate {
    var $nodeArray;
    var $nodeToVar=array();
    var $offsetToContext=array();
    var $offsetKeys;
    var $phpPhase1;
    var $prependTag;
    var $fileName;
    function __construct($str="",$prependTag="")
    {

        $this->prependTag=$prependTag;
        if($str!="")
        $this->parseString($str);
    }
    function parseString($str)
    {
    $str=trim($str);
    $offset=0;
    $len=strlen($str);
    $curNode=null;
    $nodeArray=array();
    $pointer=0;

    // Primera pasada:lineal.Lo que empiece por "[_" o "[/", es un nodo de tipo "TAG".
    // Cualquier otra cosa es un nodo texto.
    // No se distingue si son tags de cierre o de apertura;simplemente, se crea una
    // lista TEXTO-TAG-TEXTO-TAG....La ordenacion se hace en la segunda pasada.

    for($k=0;$k<$len;$k++)
    {
        $char=$str[$k];
        $isTag=0;
        if($char=="[")
        {
            // Vemos si es una referencia a un sub-widget...Estos van a formar parte del arbol tambien..
            for($m=$k+1;$str[$m]==":" || $str[$m]==";";$m++);
            if($str[$m]=="_" || $str[$m]=="/")
            {
                //$k=$m-1;
                $isTag=1;
            }
        }

        if($isTag)
        {
            if($curNode)
            {
                $nodeArray[$pointer]=$curNode;
                $pointer++;
            }
            $curNode=new CTemplateNode("TAG",$this,$k);
        }
        if(!$curNode)
        {
            $curNode=new CTemplateNode("TEXT",$this,$k);
        }

        $curNode->addChar($char);
        if($char=="]" && $curNode->type=="TAG")
        {
                $nodeArray[$pointer]=$curNode;
                $pointer++;
                $curNode=0;
        }
    }
    if($curNode)
    {
        if($curNode->length()>0)
            $nodeArray[$pointer]=$curNode;
    }
    else
    {
        $pointer--;
    }
    
    // Se inicializan los nodos, para que cada uno de ellos sepa si es un nodo
    // de tipo texto, TAG_OPEN, o TAG_CLOSE
    for($k=0;$k<=$pointer;$k++)
        $nodeArray[$k]->initialize();
    // Segunda pasada: 
    // Se recorre el array lineal.Cada vez que se encuentra un nodo de tipo cierre,
    // hay que ir hacia atras, buscando el nodo de apertura.Todos los nodos intermedios,
    // son considerados nodos hijos del actual.A la vez, estos nodos hijos tienen que
    // eliminarse del array lineal.De esta forma, se convierte el array lineal en un
    // arbol.Por ello, hay que examinar constantemente (en el while) la longitud actual
    // del array, ya que vamos a irlo recortando, a medida que convirtamos sus elementos
    // en hojas del arbol.
    $k=0;
    while($k<count($nodeArray))
    {
        $curNode=$nodeArray[$k];
        if($curNode->type=="TAG_CLOSE")
        {
            // Tenemos que ir hacia atras, y, a medida que lo hacemos, vamos aniadiendo
            // hijos a este tag.Cuando encontremos su tag_open, lo eliminamos tambien.
            // Bucle de insercion de sub-hijos.
            for($i=$k-1;$i>=0 && $curNode->parse_addChild($nodeArray[$i]);$i--);
            if($i<0)
            {
                // hubo un error.Se encontro un tag de cierre, pero no su tag de apertura.
                echo "TAG ERROR:".$curNode->tagName.": Close tag found without previous opening";
                exit();
            }
            array_splice($nodeArray,$i,($k-$i));
            $k=$i+1;
        }
        else
            $k++;
    }
    if($this->prependTag)
    {
        $rootNode=new CTemplateNode("TAG_CLOSE");
        $rootNode->tagName=$this->prependTag;
        $rootNode->closed=1;
        $rootNode->childNodes=$nodeArray;
        $rootNode->textContent="[_".$this->prependTag."]";
        $rootNode->nestLevel=0;
        $this->nodeArray=array($rootNode);
    }
    else
        $this->nodeArray=$nodeArray;
    }

    function generateVars()
    {
        $nNodes=count($this->nodeArray);
        for($k=0;$k<$nNodes;$k++)
        {
            if($this->nodeArray[$k]->type=="TEXT")
            {
                $str.=$this->nodeArray[$k]->textContent;
            }
            else
                $str.=$this->nodeArray[$k]->toVarString($this);
        }
        // Una vez generadas las variables, hay que ajustar los offsets de comienzo de todos los nodos, ya que se ha aniadido texto
        // al crear comentarios, nombres de variables, etc.En otras palabras: el calculo de los contextos se hace
        // sobre un codigo que ha aumentado, por lo que hay que ajustar ese aumento con respecto al texto del widget/template.
        // Para ello, habra que ir sobre todos los nodos, aumentando la posicion (startOffset), con el incremento de caracteres
        // que se ha ido produciendo a medida que se han hecho sustituciones.
        $increment=0;
        for($k=0;$k<$nNodes;$k++)
        {
            $increment=$this->nodeArray[$k]->adjustOffsets($increment);
        }
        // Se almacena en un fichero temporal..
        $this->phpPhase1=$str;
    }
    function addChild($node)
    {
        $this->nodeArray[]=$node;
    }
    function addNodeToVarRelationship(& $node,$varname)
    {
        $this->nodeToVar[$varName]=& $node;
    }
    
    function dump()
    {
        for($k=0;$k<count($this->nodeArray);$k++)
        {
            $this->nodeArray[$k]->dump();
        }
    }
    function nodesToText()
    {
        for($k=0;$k<count($this->nodeArray);$k++)
        {
            $str.=$this->nodeArray[$k]->nodeToText();
        }
        return $str;
    }
    function fixSyntax()
    {
        for($k=0;$k<count($this->nodeArray);$k++)
        {
            $this->nodeArray[$k]->fixSyntax();
        }
    }
    function setNodesInStringContext($str,$context)
    {
        $allmatches=preg_match_all('/(\__[a-zA-Z0-9]*__)/',$str,$matches);
        $nMatches=count($matches[1]);
        for($k=0;$k<$nMatches;$k++)
        {
            $this->nodeToVar[$matches[1][$k]]=$context;
        }
    }
    function parseInitialPHP()
    {
        //echo "<br><br><hr><br><br>";
        $this->generateVars();

        // Chequeamos que el codigo fuente sea compilable:
        /*$tFile=tempnam(getcwd(),"parse_");
        file_put_contents($tFile,$this->phpPhase1);
        $cad=PHPCLIPATH."/php.exe -l ".$tFile;
        exec($cad,$output,$returnVal);
        if(!strstr($output[0],"No syntax errors"))
        {
            echo "Errores encontrados en ".$this->fileName."<br>";
            echo implode("\n",$output);
            exit(-1);
        }
        unlink($tFile);*/
        $tokens=token_get_all($this->phpPhase1);
        $nTokens=count($tokens);
        $currentContext="HTML";
        $stateStack=array();
        $offset=0;
        //echo "<hr>".htmlentities($this->phpPhase1)."</hr>";

        for($k=0;$k<$nTokens;$k++)
        {
            if(is_array($tokens[$k]))
            {
                //echo token_name($tokens[$k][0])."...(".htmlentities($tokens[$k][1]).")".$currentContext."<br>";
                switch($tokens[$k][0])
                {
                case T_OPEN_TAG:
                    {
                        $this->offsetToContext[$offset]="PHP";
                        $currentContext="PHP";
                        array_push($stateStack,$currentContext);
                    }break;
                case T_OPEN_TAG_WITH_ECHO:
                case T_ECHO:
                    {
                        $this->offsetToContext[$offset]="VARVALUE_ECHO";
                        $currentContext="VARVALUE_ECHO";
                        array_push($stateStack,$currentContext);
                    }break;
                case T_CLOSE_TAG:
                    {
                        $this->offsetToContext[$offset]="HTML";
                        $currentContext=array_pop($stateStack);
                    }break;
                case T_INLINE_HTML:
                    {
                        // Hay que poner todos los nodos cuyas variables se encontraban en el INLINE_HTML, a este contexto.
                        $this->offsetToContext[$offset]="HTML";
                        $this->setNodesInStringContext($tokens[$k][1],"HTML");
                    }break;
                case T_VARIABLE:
                    {
                        if($currentContext=="DBLQUOTE" || $currentContext=="DBLQUOTE_ECHO")
                        {
                            $this->setNodesInStringContext($tokens[$k][1],$currentContext);
                        }
                        else
                        {
                            $this->offsetToContext[$offset]="VARIABLE";
                            $this->setNodesInStringContext($tokens[$k][1],"VARVALUE");
                        }
                    }break;
                case T_CONSTANT_ENCAPSED_STRING:
                    {
                        if($tokens[$k][1][0]=='"')
                            $pref="DBLQUOTE";
                        else
                            $pref="SNGLQUOTE";
                        if($currentContext=="VARVALUE_ECHO")
                        {
                            $this->offsetToContext[$offset]=$pref."_ECHO";
                            $this->setNodesInStringContext($tokens[$k][1],$pref."_ECHO");
                        }
                        else
                        {
                            $this->offsetToContext[$offset]=$pref;
                            $this->setNodesInStringContext($tokens[$k][1],$pref);
                        }
                    }break;
                case T_COMMENT:
                    {
                        $this->offsetToContext[$offset]="COMMENT";
                        $this->setNodesInStringContext($tokens[$k][1],$currentContext);
                    }break;
                default:
                    {
                      //  echo "token no controlado:".token_name($tokens[$k][0])."<br>";
                        $this->offsetToContext[$offset]=$currentContext;
                        $this->setNodesInStringContext($tokens[$k][1],$currentContext);
                    }
                }
                $newOffset=$offset+strlen($tokens[$k][1]);
            }
            else
            {
               // echo htmlentities($tokens[$k]).")".$currentContext."<br>";
                switch($tokens[$k])
                {
                case ";":
                    {
                        array_pop($stateStack);
                        $currentContext="PHP";
                        $this->offsetToContext[$offset]="PHP";
                        array_push($stateStack,$currentContext);
                    }break;
                case "=":
                    {
                        $this->offsetToContext[$offset]="VARVALUE";
                        array_push($stateStack,"VARVALUE");
                        $currentContext="VARVALUE";
                    }break;
                    case "'":
                    {
                        $lastChar="'";
                        if($currentContext=="SNGLQUOTE" || $currentContext=="SNGLQUOTE_ECHO")
                        {
                            $currentContext=array_pop($stateStack);
                            $this->offsetToContext[$offset]=$currentContext;
                        }
                        else
                        {
                            if($currentContext=="VARVALUE_ECHO")
                                $state="SNGLQUOTE_ECHO";
                            else
                                $state="SNGLQUOTE";
                            $this->offsetToContext[$offset]=$state;
                            array_push($stateStack,$state);
                            $currentContext=$state;
                        }
                    }break;
                case '"':
                    {
                        $lastChar='"';
                        if($currentContext=="DBLQUOTE" || $currentContext=="DBLQUOTE_ECHO")
                        {
                            $currentContext=array_pop($stateStack);
                            $this->offsetToContext[$offset]=$currentContext;
                        }
                        else
                        {
                            if($currentContext=="VARVALUE_ECHO")
                                $state="DBLQUOTE_ECHO";
                            else
                                $state="DBLQUOTE";
                            $this->offsetToContext[$offset]=$state;
                            array_push($stateStack,$state);
                            $currentContext=$state;
                        }
                    }break;
                case '.':
                    {
                        /*
                        if($currentContext=="VARVALUE")
                            echo "EN VARVALUE";*/
                    }break;
                case '[':
                    {
                        array_push($stateStack,$currentContext);
                        $currentContext="ARRAY_INDEX";
                    }break;
                case ']':
                    {
                        $currentContext=array_pop($stateStack);
                    }break;
                 default:
                 {
                    // echo "ENCONTRADO NO ARRAY:".$tokens[$k]."<br>";
                 }
                }                
                $newOffset=$offset+strlen($tokens[$k]);
            }
            
            $offset=$newOffset;

        }
        if(is_array($this->offsetToContext))
           $this->offsetKeys=array_keys($this->offsetToContext);
        else
            $this->offsetKeys=array();
        // Resolvemos todos los nodos
        for($k=0;$k<count($this->nodeArray);$k++)
            $this->nodeArray[$k]->resolveContext();

    }
}


class CTemplateParser {
    var $templatePath;
    var $addTemplatePath;
    function __construct($templatePathAdd=array(),$templatePath="")
    {
        if($templatePath=="")
        {
            $this->templatePath=array(dirname(__FILE__)."/widgets");
        }
        else
            $this->templatePath=(array)$templatePath;
        $this->templatePathAdd=$templatePathAdd;

    }
    function parseTemplate($str)
    {
        
        // Esta es una template pura.Sin widgets.
        $t=new CTemplate($str);
        return $t->parseInitialPHP();
        // Segunda pasada : sustitucion de tags por variables.
    }
    function solveTemplate($str,$addedPath=array())
    {
        //echo htmlentities($str);
        $this->addTemplatePath=$addedPath;
        $t=new CTemplate($str);
        
        $t->parseInitialPHP();
        
        //echo htmlentities($t->phpPhase1);
       /* $blendedResult=$this->blendSyntax($result);
        $newStr=$blendedResult->nodesToText();
        echo "DUMPING:<br>";
        echo "<hr>";*/
        // Se comprueba que todos los nodos de blendedresult sean del tipo texto
        //echo "INITIALSOLVE DE SOLVETEMPLATE<br>";
        $result=$this->initialSolve($t->nodeArray);
        //$result->dump();
        
        $result->fixSyntax();
        //echo "<br><br>RESULTADO:<br><div style='background-color:yellow'>";
        //echo htmlentities($result->nodesToText());
        //echo "</div>";
        return $result;
        
    }
    private function initialSolve(& $t,$nestLevel=0)
    {
        
        $nestLevel=0;
        $templateNodes = & $t;
        
        do{
            // Se itera sobre los resultados, hasta que se solucionen todos los niveles
            // de profundidad
            $result = new CTemplate();
            
            $nNested=$this->solveNodes($templateNodes,null,$result,$nestLevel);
            
            $templateNodes=$result->nodeArray;
            //$result->dump();
            $nestLevel++;
        }while($nNested > 0);
        $result=new CTemplate();
        $result->nodeArray=$templateNodes;
        
        return $result;
    }

    function solveNodes($templateNode,$widgetNodes,& $resultNode,$level)
    {
        $nTemplateNodes=count($templateNode);
        if($widgetNodes==null)
        {
            $nNestedNodes=0;
            // Se ha llamado a solveNodes sin un widget.Hay que copiar los nodos de texto al resultado,
            // y los nodos de la template que sean un tag, deben ser considerados nuevos widgets a cargar.
            for($k=0;$k<$nTemplateNodes;$k++)
            {
                $curTNode=& $templateNode[$k];
                // Si lo que encuentro es un nodo de texto, simplemente, se copia al resultado.
                if($curTNode->type=="TEXT")
                {
                    $newNode=$curTNode->duplicate(0);
                    $newNode->parsed=1;
                    $resultNode->addChild($newNode);
                    continue;
                }
                // Si lo que encuentro es un tag, primero vemos si ya ha sido parseado.En 
                // ese caso, llamamos a solveNodes, con su contenido.
                if($curTNode->parsed==1)
                {
                    $newNode=$curTNode->duplicate(0);
                    $nNestedNodes+=$this->solveNodes($curTNode->childNodes,null,$newNode,$level);
                    $resultNode->addChild($newNode->duplicate(1));
                    continue;
                }
                if($curTNode->nestLevel > $level)
                {
                    $newNode=$curTNode->duplicate(0);
                    $nNestedNodes++;
                    $nNestedNodes+=$this->solveNodes($curTNode->childNodes,null,$newNode,$level);
                    $resultNode->addChild($newNode->duplicate(1));
                    continue;
                }

                if($curTNode->nestLevel == $level)
                {
                        // Tenemos un widget asociado a este mismo nivel.Hay que cargarlo, y establecerlo como widget actual.
                        $newWidget=$this->loadWidget($curTNode->tagName);
                           
                        if(!$newWidget)
                        {
                            echo "ERROR : Widget not found : ".$curTNode->tagName;
                            return -1;
                        }
                        $newWidget->parseInitialPHP();
                        $newNode=$curTNode->duplicate(0);
 
                         
                        $nNestedNodes+=$this->solveNodes($curTNode->childNodes,$newWidget->nodeArray,$newNode,0);                        
                        $newNode->parsed=1;
                        $newNode->widgetRoot=$curTNode->tagName;
                        $resultNode->addChild($newNode->duplicate(1));
                }
            }
            
            return $nNestedNodes;
        }
        $nWidgetNodes=count($widgetNodes);
        $nNestedNodes=0;
        
        for($k=0;$k<$nWidgetNodes;$k++)
        {
            
            
            $curWNode=& $widgetNodes[$k];
            if($curWNode->type=="TEXT")
            {
                //echo "Agregando nodo de texto:".htmlentities($curWNode->textContent)."<br>";
                if(trim($curWNode->textContent,"\r\n \t")=="")
                            continue;
                $tNode=$curWNode->duplicate();
                $tNode->parsed=1;
                $tNode->destContext=$tNode->getContext();
                $resultNode->addChild($tNode);
            }
            else
            {
                if($curWNode->nestLevel>$level && !$curWNode->shared)
                {
                    // Nodo perteneciente a un sub-widget llamado desde el widget principal.
                    // Simplemente, lo agregamos al resultado, nos movemos a sus hijos, manteniendo templateNode.
                    // NO se considera como "solved"
                    $newResultNode=$curWNode->duplicate(0);
                    if(count($curWNode->childNodes) > 0)
                    {
                        $nNestedNodes+=$this->solveNodes($templateNode,$curWNode->childNodes,$newResultNode,$level);
                    }
                    $resultNode->addChild($newResultNode);
                    $nNestedNodes++;
                    continue;
                }

                if($curWNode->tagName=="*")
                //if(!$curWNode->closed)
                {
                    $t=$this->initialSolve($templateNode,$level);
                    // Se duplica el nodo *, para marcar su situacion
                    $newNode=$curWNode->duplicate(0);
                    $newNode->parsed=1;
                    $newNode->srcContext=$curWNode->getContext();                    
                    $nNewNodes=count($t->nodeArray);
                    for($h=0;$h<$nNewNodes;$h++)
                    {
                        /* Se duplican los nodos que provienen de la template, y se introducen */
                        $m=$t->nodeArray[$h]->duplicate(1);
                        $m->srcContext=$newNode->srcContext;
                        $m->destContext=$m->getContext();
                        $m->parsed=1;
                        $newNode->addChild($m);
                    }
                    $resultNode->addChild($newNode);
                    continue;
                }
                for($j=0;$j<$nTemplateNodes;$j++)
                {
                    $curTemplateNode=$templateNode[$j];
                    $newResultNode=$curWNode->duplicate(0);
                    $newResultNode->parsed=1;
                    if($curTemplateNode->tagName==$curWNode->tagName)
                    {
                        // Es bastante parecido a lo que ocurre en "*".La diferencia es que
                        // no se envia toda la template, sino solo los hijos del nodo de la
                        // template que hizo match con este tag.
                    if($curWNode->type=="TAG_OPEN")
                    {
                        // Nada mas empezar, si era un nodo shared, se duplica, y se introduce en el resultado,
                        // pero indicando que ya no es shared.
                        if($curWNode->shared==1)
                        {
                            $curWNode->shared=0;
                            $newWNode=$curWNode->duplicate(0);
                            $newWNode->shared=0;
                            $resultNode->addChild($newWNode);
                            $nNestedNodes++;
                        }

                        $t=$this->initialSolve($curTemplateNode->childNodes,0);
                        $newNode=$curWNode->duplicate(0);
                        
                        $newNode->srcContext=$curWNode->getContext();
                        $newNode->parsed=1;
                        $nNewNodes=count($t->nodeArray);
                        for($h=0;$h<$nNewNodes;$h++)
                        {
                            $m=$t->nodeArray[$h]->duplicate(1);
                            $m->srcContext=$newNode->srcContext;
                            $m->destContext=$m->getContext();
                            $m->parsed=1;
                            $newNode->addChild($m);
                        }
                        $resultNode->addChild($newNode);
                        /*if($curTemplateNode->type=="TEXT")
                        {
                            $newResultNode->addChild($curTemplateNode->duplicate(1));
                            $resultNode->addChild($newResultNode->duplicate(1));
                        }
                        else
                        {
                            $nNestedNodes+=$this->solveNodes($curTemplateNode->childNodes,$curWNode->childNodes,& $newResultNode,$level);
                            $oNode=$newResultNode->duplicate(1);
                            $oNode->srcContext=$curWNode->getContext();
                            $oNode->destContext=$curTemplateNode->getContext();
                            $resultNode->addChild($oNode);
                        }*/
                    }
                    else
                    {
                        $isShared=0;
                        if($curWNode->shared==1)
                        {
                            $newCWNode=$curWNode->duplicate(0);
                            $newCWNode->shared=0;
                            $newResultNode->shared=0;
                            $isShared=1;
                            $nNestedNodes++;
                        }
                        $nNestedNodes+=$this->solveNodes($curTemplateNode->childNodes,$curWNode->childNodes,& $newResultNode, $level);
                        $oNode=$newResultNode->duplicate(1);
                        $oNode->parsed=1;
                        $oNode->srcContext=$curWNode->getContext();
                        $oNode->destContext=$curTemplateNode->getContext();
                        $resultNode->addChild($oNode);
                        if($isShared)
                            $resultNode->addChild($newCWNode);
                        
                        /*$curWNode->dump();
                        $t=$this->initialSolve($curTemplateNode->childNodes);
                        echo "TRAS EVALUAR EL CONTENIDO de:".$curWNode->tagName;
                        $t->dump();
                        $nNewNodes=count($t->nodeArray);
                        for($h=0;$h<$nNewNodes;$h++)
                            $newResultNode->addChild($t->nodeArray[$h]->duplicate());
                        $resultNode->addChild($newResultNode->duplicate(1));
                        */
                    }
                    }
                    
                }

            }
        }
        return $nNestedNodes;
    }

    function & loadWidget($tagName)
    {
        $path=$this->searchWidget($tagName,$this->addTemplatePath);
        if(!$path)
            $path=$this->searchWidget($tagName,$this->templatePath);

        if(!$path)
            return 0;
        $p=new CTemplate(file_get_contents($path));
        $p->fileName=$path;
        return  $p;
    }
    function searchWidget($name,& $paths)
    {
        for($k=0;$k<count($paths);$k++)
        {
            if(is_file($paths[$k]."/".$name.".html"))
                return $paths[$k]."/".$name.".html";
        }
        return 0;
    }
}
?>
Return current item: Siviglia Templating