<?php

//  Template Engine
//  ---------------
//
// Tento modul zpracovává tmplejty a strká do nich dynamicky geenrované
// informace a vytváří tak dynamické HTML stránky s oddělenou logikou
// programu a vzhledu.
//
// Více viz popis činnosti v konentáři na začátku templejtu
//
// (c)miho www.mlab.cz 2007, free software
//
// Typické použití objektu je toto:
//
//      - Založ objekt templejtu (new)
//      - $Template = new TemplateEngine();
//
//      - Načti templejt ze souboru do objektu (ošetři chybu!)
//      - $error = $Template->LoadTemplate($TemplateFile);
//
//      - Tady se provádí generování masa, je možné použít proměnné,
//      - modifikovat proměnné a nechat provéstsubstituce v proměnných
//      - do řetězců.
//
//          $Template->SetVariable('InfoTitle','Generovaný seznam');
//          - první parametr je jméno proměnné, druhý je jeho nový obsah
//
//          $Line = $Template->SetVariable('InfoLine')
//          - vrací obsah proměnné nebo prázdný řetězec
//
//          - Připrav si proměnné, které se budou strkat do vzorového řetězce
//          $Variables[InfoName]    = 'nějaká informace pro proměnnou InfoName';
//          $Variables[InfoPicture] = 'jiná informace pro proměnnou InfoPicture';
//          - aplikuj (nahraď) proměnné na vzorový řetězec z propměnné 'InfoLine'
//          $Line = $Template->Substitute($Variables, $Template->GetVariable('InfoLine'));
//
//      - Nakonec se naaplikují proměnné do nataženého templejtu a výsledek
//      - se pošle klientovi
//      echo $Template->GetPage();
//


// Objekt pro zpracování templejtu
class TemplateEngine
{

  private $Page;      // Sem se načte templejt a zde se provádí substituce
  private $Info;      // Sem se načtou z templejtu proměnné


  function ListInfo()
  // Vypíše nalezené proměnné a jejich hodnoty
  // Pouze pro ladění
  {
    echo "[ ListInfo:\n";
    print_r($this->Info);
    echo ']';
  }


  function LoadTemplate($TemplateFileName, $Lang)
  // Načte templejt ze souboru, při chybě vrací chybový řetězec
  // Snaží se rozumně využít informaci o jazyku k otevření správné verze
  // templejtu. Cesta se zadává bez jazykové části ale s příponou.
  {
    // Vlož zadaný jazyk do názvu souboru
    $TemplateFileName = LangFile($TemplateFileName, $Lang);

    // Načti template ze souboru (po řádcích do pole)
    $this->Page = @file($TemplateFileName);
    if ($this->Page=='')
      return 'Unable Open Template File';

    // Sestav z pole jeden retězec
    $this->Page = implode($this->Page);


    // Definice regulárních výrazů pro hledání
    // ---------------------------------------

    // Definice vzoru pro hledání komentáře ve tvaru <!-- -->
    //   Komentář je nepojmenované pole
    //   Před a za koentářem mohou být bílé znaky
    //   Uvnitř komentáře nesmí být znak >
    $PatternComment = '(?:\s*<!--[^>]*--\s*>\s*)';
        //  (?:         nepojemnované pole
        //    \s*       volitelné bílé znaky před
        //    <!--      začátek komentáře
        //    [^>]*     vniřek komentáře (neobsahuje >)
        //    --\s*>    konec komentáře
        //    \s*       volitelné bílé znaky za komentářem
        //  )           tady končí komentář

    // Definice vzoru pro hledání proměnných ve tvaru $InfoXXX
    $PatternVar ='\$(?P<VarName>Info[a-z,A-Z,0-9,\-\_]+)';
        //  \$                            na začátku $
        //  (?P<VarName>                  pojmenované pole VarName je (jméno proměnné bez dolaru)
        //    Info[a-z,A-Z,0-9,\-\_]+     proměnná začíná Info a pak jsou písmena,
        //  )                               číslice, mínus a podtržítko

    // Definice vzoru pro hledání hodnot proměnných ve tvaru = " Hodnota "
    // Definice hodnot je ve tvaru $InfoXXX="YYY" a mohou být víceřádkové
    // ( a mohou obsahovat uvnitř další substituce uvozené $,  které program
    // zpracovává ale ne tady)
    $PatternValue = '\s*=\s*"(?P<VarValue>[^"]*)"';
        //  \s*=\s*                       rovná se volitelnými bílými znaky okolo
        //  "                             hodnota v uvozovkách
        //  (?P<VarValue>                 parametr s názvem VarValue
        //    [^"]*                         hodnota nesmí obsahovat uvozovky
        //  )                             vracíme hodnotu bez uvozovek
        //  "                             konec uvozovek

    // Zparacování vzoru
    // -----------------

    // Najdi na začátku stránky komentář a vykousni ho
    $pattern = '/^'  .$PatternComment.  '+/su';
    $this->Page = preg_replace($pattern, '', $this->Page);
        //  /                     oddělovač
        //  ^                     od začátku řetězce (jen komentáře na začátku souboru)
        //  $PatternComment       komentář (<!-- -->) s bílými znaky okolo
        //  +                     alespoň jeden komentář
        //  /su                   modifikátor, . zastupuje i odřádkování, utf8


    // Najdi v templejtu substutuční proměnné a vytvoř v poli $Info
    // prázdné položky pro každou použitou proměnnou. Dělá se to proto,
    // aby se na konci nepoužité proměnné nahradily prázdným řetězcem.
    $pattern    = '/' .$PatternVar. '/su';
    preg_match_all($pattern, $this->Page, $temp);
        //  /                     oddělovač
        //  .$PatternVar.         proměnná ( $InfoXXX ) - parametr VarName
        //  /su                   modifikátor zajistí, že . zastupuje i odřádkování, utf8
    foreach($temp['VarName'] as $key => $value)
      $this->Info[$value] = '';
    unset($temp);

    // Vytáhni proměnné a jejich hodnoty do pole $Info
    $pattern = '/' .$PatternVar.$PatternValue. '/su';
        //  /                     oddělovač
        //  .$PatternVar.         proměnná ( $InfoXXX )       - parametr VarName
        //  .$PatternValue.       přiřazení ( = " Hodnota " ) - parametr VarValue
        //  /su                   modifikátor, . zastupuje i odřádkování, UTF8
    preg_match_all($pattern, $this->Page, $temp);
    foreach($temp['VarName'] as $key => $value)
      $this->Info[$value] = $temp['VarValue'][$key];
    unset($temp);

    // Vykousej zpracované definice proměnných (a komentáře před nimi)
    $pattern = '/' .$PatternComment.'?'.$PatternVar.$PatternValue. '/su';
        //  /                     oddělovač
        //  .$PatternComment.'?'  volitelný komentář před definicí
        //  .$PatternVar.         proměnná ( $InfoXXX )
        //  .$PatternValue.       přiřazení ( = " Hodnota " )
        //  /su                   modifikátor, . zastupuje i odřádkování, UTF8
    $this->Page = preg_replace($pattern, '', $this->Page);
  }
  
  function Indent($StringData, $Indent)
  // Všechna odřádkování v řetězci $StringData odsadí o hodnotu $Indent pokud je
  // $Indent číslo nebo o déllku řetězce $Indent pokud je to řetězec
  {
    // Velikost odsazení (vyrob číslo)
    if (is_string($Indent))
      $Num = mb_strlen($Indent,'utf-8');
    else
      $Num = 0+$Indent;

    // Odsazení
    return $StringData = $Indent.str_replace("\n", "\n".str_repeat(' ', $Num), $StringData);
  }


/*
  function Substitute($Variables, $String)
  // Nahradí proměnné v řetězci $String obsahem proměnných z pole $Variables
  // a vrátí výsledný řetězec
  {
    if (is_array($Variables))
    {
      foreach($Variables as $Key => $Value)
      {
        // Hledací vzor (hledáme proměnnou s názvem v $Key)
        $Pattern = '\$'.$Key.'(?=[^a-z,A-Z,0-9,\-\_])';
            //    \$$Key                      hledáme $JmenoProměnné
            //    (?=                         následuje něco, co nemůže být jménem
            //        [^a-z,A-Z,0-9,\-\_]     a nezajímá nás
            //    )

        // Vkládáme více řádek?
        $IsMultiline = !(strpos($Value, "\n") === FALSE);
        
        if (!$IsMultiline)
        {
          // Vkládáme jednoduchý řetězec
          $String = preg_replace('/'.$Pattern.'/su', $Value, $String);
              //    /su                         víceřádkové v UTF8
        }
        else
        {
          // Vkládáme řetezec s odřádkováním - je třeba odsazovat
          $String = preg_replace('/(.*)'.$Pattern.'/ue',
                                 "\$this->Indent(\$Value, '\\1')",
                                 $String);
              //    (.*)                        hledáme cokoli před vzorem -> první parametr
              //    .$Pattern.                  proměnná
              //    /ue                         UTF8, náhrada je php skriptem
        }
      }
    }
    
    return $String;
  }
*/


  function Substitute($Variables, $String)
  // Nahradí proměnné v řetězci $String obsahem proměnných z pole $Variables
  // a vrátí výsledný řetězec
  {
    // Hledací vzor
    $Pattern = '/(.*?)\$(Info[a-z,A-Z,0-9,\-\_]+)(?=[^a-z,A-Z,0-9,\-\_])/ue';

    // Náhrada s kusem php kódu (nezapomeň, že preg_replace s přepínačem /e vrací oescapované řetězce)
    $String = preg_replace($Pattern,
                           'isset($Variables[\'\2\'])
                            ? $this->Indent($Variables[\'\2\'], stripslashes(\'\1\'))
                            : stripslashes(\'\1\')
                           ',
                           $String);
    return $String;
  }


  function SetVariable($VarName, $VarValue, $Wrap=0)
  // Nastaví hodnotu proměnné
  // Volitelně provede zálámání na zadaný počet znaků a odstranění nadbytečných mezer
  {
    // Volitelné zalámání a odstranění bílých znaků
    if ($Wrap!=0)
      $VarValue = WrapString($VarValue, $Wrap);
    
    // Uložení do proměnných
    $this->Info[$VarName] = $VarValue;
  }
  

  function GetVariable($VarName)
  // Vrátí hodnotu proměnné tak, jak je, včetně mezer a odřádkování
  {
    if (isset($this->Info[$VarName]))
      return $this->Info[$VarName];
    else
      return '';
  }
  

  function GetString($VarName)
  // Vrátí parametr jako jeden řetězec s redukovanými bílými znaky
  // Tedy bez odřádkování a nadbytečných mezer
  {
    return WrapString($this->GetVariable($VarName), 0);
  }

  
  function GetPage()
  // Provede substituce ve stránce a vrátí ji jako řetězec
  // Substituce bere z proměnné $Info
  {
    // Proveď ve stránce substituce proměnných a vrať výsledek
    return $this->Substitute($this->Info, $this->Page);
  }
}


?>