За последние 24 часа нас посетили 20283 программиста и 1084 робота. Сейчас ищут 743 программиста ...

Шаблонизатор "Дружба"

Тема в разделе "Решения, алгоритмы", создана пользователем Mr.M.I.T., 9 ноя 2008.

  1. Mr.M.I.T.

    Mr.M.I.T. Старожил

    С нами с:
    28 янв 2008
    Сообщения:
    4.586
    Симпатии:
    1
    Адрес:
    у тебя канфетка?
    Я смотрю есть люди которые заинтересовались моим первым шаблонизотором
    хотя я его написал просто от нефиг делать и чтобы потренероваться
    решил создать тему посвешённую ему

    Шаблозитор назван Дружба =))
    потому что его синтаксис представляет нечто среднее между натив пхп и смарти,
    короче в честь дружбы сторонником шаблонизаторов и их противников,
    и вообще СССР Фореве!

    Да код немного кривой, но это мой первый динамический шаблонизатор

    вот оно!
    PHP:
    1. <?
    2. /*
    3. ---------------------------------------
    4. Script - Шаблонизатор "Дружба"
    5. ---------------------------------------
    6. Coded - By Mr.M.I.T.
    7. ---------------------------------------
    8. */
    9. <?
    10. class tpl {
    11.    public static $tpldir,$cachedir,$cachetime;
    12.    private $data,$vars,$whoreplace,$replacer,$result;
    13.    function __construct() {
    14.        if (!self::$tpldir)
    15.           $this->error("Ошибка: Не указана папка с шаблонами");
    16.        if (!self::$cachedir)
    17.           $this->error("Ошибка: Не указана папка для кеширования шаблонов");
    18.    }
    19.    function error($msg) {
    20.        trigger_error($msg,E_USER_ERROR);
    21.    }
    22.    // Записывает переменную
    23.    // $name может быть массивом
    24.    function set($name,$value=0) {
    25.        if (is_array($name) && count($name)) {
    26.            foreach($name as $key=>$val) {
    27.                $this->vars[$key]=$val;
    28.            }
    29.        } else if (!is_array($name)){
    30.            $this->vars[$name]=$value;
    31.        }else {
    32.            $this->error("Ошибка: Не указано значение переменной");
    33.        }
    34.    }
    35.   // Шаблоном может быть строка
    36.   // но тогда никакого кеширования не будет
    37.    function loadStr($str) {
    38.        $this->data=$str;
    39.        $this->compiler();
    40.        return $this->result;
    41.    }
    42.   // возвращает результат парсинга
    43.   // $tpl название шаболона
    44.    function load($tpl) {
    45.     if(!$this->is_cached($tpl)) {
    46.        $fp=file_get_contents(self::$tpldir.'/'.$tpl);
    47.        if ($fp) {
    48.            $fp=get_magic_quotes_gpc()?stripslashes($fp):$fp;
    49.            $this->data=$fp;
    50.            $this->compiler();
    51.            $this->savecache($tpl);
    52.        }else {
    53.            $this->error("Ошибка: Шаблон $tpl не найден");
    54.        }
    55.      }else {
    56.         $this->compilecache($tpl);
    57.      }
    58.       return $this->result;
    59.    }
    60.     // Очищает переменные, $var может быть массивом
    61.    function clear($var) {
    62.        if (!is_array($var)) {
    63.           if ($this->vars[$var])
    64.               unset($this->vars[$var]);
    65.        }else {
    66.            for($i=0,$c=count($var);$i<$c;$i++) {
    67.               if ($this->vars[$var[$i]])
    68.                 unset($this->vars[$var[$i]]);
    69.             }
    70.         }
    71.      }
    72.  // достаёт шаблон из кеша
    73.    private function compilecache($tpl) {
    74.       @ob_start();
    75.          extract($this->vars);
    76.          include self::$cachedir."/".md5($tpl).".tpl.php";
    77.          $this->result=ob_get_contents();
    78.       ob_clean();
    79.    }
    80.   // проверяет есть ли шаблон в кеше
    81.    private function is_cached($tpl) {
    82.       $dir=self::$cachedir."/".md5($tpl).".tpl.php";
    83.       if(file_exists($dir)) {
    84.           if (filemtime($dir)+self::$cachetime>=time()) {
    85.               return true;
    86.           }else {
    87.               unlink($dir);
    88.           }
    89.       }
    90.       return false;
    91.    }
    92.   // компилятор
    93.    private function compiler() {
    94.        $sys=$this->parseSys();
    95.        for($i=0,$c=count($sys);$i<$c;$i++) {
    96.            $this->whoreplace[]="{".$sys[$i]."}";
    97.            switch($this->TypeStr($sys[$i])) {
    98.                case"func":
    99.                   $this->parseFunc($sys[$i]);
    100.                break;
    101.                case"var":
    102.                   $this->parseVar($sys[$i]);
    103.                break;
    104.                case"closed":
    105.                   $this->parseClosed();
    106.                break;
    107.                case"else":
    108.                  $this->parseElse();
    109.                break;
    110.                case false:
    111.                  $this->error("Неизвестный параметр {{$sys[$i]}}");
    112.                break;
    113.                default:
    114.                   $this->parseSpec($sys[$i]);
    115.            }
    116.        }
    117.       $this->data=str_replace($this->whoreplace,$this->replacer,$this->data);
    118.       @ob_start();
    119.          extract($this->vars);
    120.          eval("?".">".$this->data."<?");
    121.          $this->result=ob_get_contents();
    122.       ob_clean();
    123.    }
    124.   // сохраняет шаблон в кеш  
    125.    private function savecache($tpl) {
    126.        $dir=self::$cachedir."/".md5($tpl).".tpl.php";
    127.         file_put_contents($dir,$this->data);
    128.         return true;
    129.    }
    130.   // список функция которые сами выводят результат, короче функции к которым не надо дописывать echo
    131.    private function getPrinFunc() {
    132.        return array(
    133.                  "print_r",
    134.                  "print",
    135.                  "echo",
    136.                  "var_dump",
    137.                  "var_export",
    138.                  "eval", // пцц =))
    139.                  "include",
    140.                  "unset",
    141.                  "preg_match_all",
    142.                  "preg_match",
    143.                  "die",
    144.                    );
    145.    }
    146.    private function parseFunc($f) {
    147.        preg_match("#(?<!\/)([\w]+)[\s]*\((.+)\)#iU",$f,$fn);
    148.        $bz=preg_match('#\$([\w]+)\=#i',$f);
    149.        if ($bz || in_array(strtolower($fn[1]),$this->getPrinFunc()))
    150.           $this->replacer[]="<?$f;?>";
    151.        else
    152.          $this->replacer[]="<? echo $f;?>";
    153.  
    154.    }
    155.    private function parseVar($v) {
    156.       $cmd=preg_match('#\$([\w]+)(.*?)(\=|\+|\-)#i',$v);
    157.       if(!$cmd)
    158.          $this->replacer[]="<? echo $v;?>";
    159.       else
    160.          $this->replacer[]="<?$v;?>";
    161.    }
    162.   // список строк, к которым есть свои парсеры
    163.    private function getUnMatch() {
    164.        return array("else");
    165.    }
    166.    private function parseSpec($str) {
    167.        $this->replacer[]="<?$str{?>";
    168.    }
    169.    private function parseClosed() {
    170.        $this->replacer[]="<?}?>";
    171.    }
    172.    private function parseElse() {
    173.        $this->replacer[]="<?}else{?>";
    174.    }
    175.   // возвращает типы строк
    176.    private function TypeStr($str) {
    177.        if (in_array($str,$this->getUnMatch()))
    178.           return $str;
    179.          
    180.        if (preg_match("#(?<!\/)([\w]+)[\s]*\((.+)\)#iU",$str,$par)) {
    181.            if(!in_array($par[1],$this->getClosed()))
    182.                return 'func';
    183.            else
    184.                return $par[1];
    185.        }
    186.        
    187.         if (preg_match("#(?<=\/)([\w]+)#i",$str,$par))
    188.           return 'closed';
    189.  
    190.        if (preg_match('#\$[\w]+#i',$str))
    191.           return 'var';
    192.          
    193.        return false;
    194.    }
    195.   // функции к которым нужно дописывать фигурные скобки
    196.    private function getClosed() {
    197.        return array(
    198.                  "foreach",
    199.                  "if",
    200.                  "for",
    201.                  "while",
    202.                    );
    203.    }
    204.   // возвращает строки для обработки
    205.    private function parseSys() {
    206.        preg_match_all("#{(.+?)}#i",$this->data,$res);
    207.        return $res[1];
    208.    }
    209. }
    210.  
    211. tpl::$tpldir='tpl';        // папка с шаблонами
    212. tpl::$cachedir="ctpl";  // папка с откомпилированными шаблонами
    213. tpl::$cachetime=60;  // время обновления кеша
    214. $tpl=new tpl();
    215. $tpl->set(array("arr"=>array("key1","key2",'key3')));
    216. $tpl->set("title","Загаловок");
    217. print $tpl->load('tpl.tpl');
    218. ?>
    219.  
    листинг шаблона tpl.tpl
    HTML:
    1. {$title}<br>
    2. {if($arr)}
    3.  {$z=0}
    4.  {$array=array("123","12312","123123")}
    5.  {for($i=0;$i<count($arr);$i++)}
    6.   {$z++}
    7.   {$arr[$i]}
    8.   {if ($z!==count($arr))}<hr>{/if}
    9.    <br>
    10.  {/for}
    11.  {foreach ($array as $key=>$val)}
    12.    {$array[$key]="prefix_".$val}
    13.  {/foreach}
    14.  {print_r($array)}
    15. {else}
    16.    =)))))
    17. {/if}
    18.  
    19. // А теперь отожом!
    20.  
    21. {include("db.class.php")}
    22. {db::$cnf=array("db_pass"=>'','db_host'=>'localhost','db_pref'=>'test','db_name'=>'test','db_user'=>'root')}
    23. {$db=new db()}
    24. {$row=$db->super_query("SELECT * FROM ".$db->pref."_test")}
    25. {for($i=0,$c=count($row);$i<$c;$i++)}
    26.  {print_r($row)}<br>
    27. {/for}
    Зы. Тестил только основные возможности

    На выходе

    PHP:
    1. <? echo $title;?><br>
    2. <?if($arr){?>
    3.  <?$z=0;?>
    4.  <?$array=array("123","12312","123123");?>
    5.  <?for($i=0;$i<count($arr);$i++){?>
    6.    <?$z++;?>
    7.    <? echo $arr[$i];?>
    8.    <?if ($z!==count($arr)){?><hr><?}?>
    9.    <br>
    10.  <?}?>
    11.  <?foreach ($array as $key=>$val){?>
    12.    <?$array[$key]="prefix_".$val;?>
    13.  <?}?>
    14.  <?print_r($array);?>
    15. <?}else{?>
    16.    =)))))
    17. <?}?>
    18. <?include("db.class.php");?>
    19. <?db::$cnf=array("db_pass"=>'','db_host'=>'localhost','db_pref'=>'test','db_name'=>'test','db_user'=>'root');?>
    20. <?$db=new db();?>
    21. <?$row=$db->super_query("SELECT * FROM ".$db->pref."_test");?>
    22. <?for($i=0,$c=count($row);$i<$c;$i++){?>
    23.   <?print_r($row);?><br>
    24. <?}?>
    Комментарии будут чуть позже,
    когда время будет напишу, пока только пример

    По правде говоря, сам я врятли его буду когда-нибудь использовать...хотя как знать ;)
     
  2. Hight

    Hight Старожил
    Команда форума Модератор

    С нами с:
    5 мар 2006
    Сообщения:
    7.153
    Симпатии:
    0
    Адрес:
    из злой параллельной вселенной
    Сложно =)

    +1
     
  3. ....успешно вобравшее в себя недостатки обоих подходов...
     
  4. Mr.M.I.T.

    Mr.M.I.T. Старожил

    С нами с:
    28 янв 2008
    Сообщения:
    4.586
    Симпатии:
    1
    Адрес:
    у тебя канфетка?
    Олег, блин, расскусил =))
    я вообще сам использую натив и не парюсь, тем более я уже где-то писал во сколько я по скорости выиграл по сравнению со смарти...
     
  5. [vs]

    [vs] Суперстар
    Команда форума Модератор

    С нами с:
    27 сен 2007
    Сообщения:
    10.553
    Симпатии:
    631
    Синтаксис сложный, зато компиляция хорошая ))
     
  6. Sergey89

    Sergey89 Активный пользователь

    С нами с:
    4 янв 2007
    Сообщения:
    4.796
    Симпатии:
    0
    Название напомнило бензопилу =)
     
  7. Петр

    Петр Активный пользователь

    С нами с:
    20 мар 2006
    Сообщения:
    1.253
    Симпатии:
    0
    Адрес:
    Центр Вселенной
    Угу, двуручную =)))
     
  8. Вльдемар

    Вльдемар Активный пользователь

    С нами с:
    20 май 2006
    Сообщения:
    635
    Симпатии:
    0
    Адрес:
    Белхород
    Двуручная называется "Дружба-2" ))
    Во всяком случае я ее так всю жизнь называю
     
  9. Mr.M.I.T.

    Mr.M.I.T. Старожил

    С нами с:
    28 янв 2008
    Сообщения:
    4.586
    Симпатии:
    1
    Адрес:
    у тебя канфетка?
    чё сразу пилы,
    больше на лесопилку похоже =))
     
  10. Mr.M.I.T.

    Mr.M.I.T. Старожил

    С нами с:
    28 янв 2008
    Сообщения:
    4.586
    Симпатии:
    1
    Адрес:
    у тебя канфетка?
    обновил код,
    теперь более человеческая компиляция..
     
  11. md5

    md5 Активный пользователь

    С нами с:
    29 янв 2007
    Сообщения:
    250
    Симпатии:
    0
    где высокая культура комментариев? (ц)
     
  12. Mr.M.I.T.

    Mr.M.I.T. Старожил

    С нами с:
    28 янв 2008
    Сообщения:
    4.586
    Симпатии:
    1
    Адрес:
    у тебя канфетка?
    ваще лень так писать =((
     
  13. Mr.M.I.T.

    Mr.M.I.T. Старожил

    С нами с:
    28 янв 2008
    Сообщения:
    4.586
    Симпатии:
    1
    Адрес:
    у тебя канфетка?
    а вот, кое-что написал
     
  14. md5

    md5 Активный пользователь

    С нами с:
    29 янв 2007
    Сообщения:
    250
    Симпатии:
    0
    ну самое-то главное не лень же было написать — кем coded by :)
     
  15. Mr.M.I.T.

    Mr.M.I.T. Старожил

    С нами с:
    28 янв 2008
    Сообщения:
    4.586
    Симпатии:
    1
    Адрес:
    у тебя канфетка?
    я вот теперь в раздумьях, юзать мне натив пхп или вот этот?...
     
  16. ShamahN

    ShamahN Активный пользователь

    С нами с:
    10 апр 2007
    Сообщения:
    1.449
    Симпатии:
    0
    Адрес:
    г.Волгодонск Роствской обл.
    Что-то я гоню.. А что делать с тем, что получается на выходе?
     
  17. ShamahN

    ShamahN Активный пользователь

    С нами с:
    10 апр 2007
    Сообщения:
    1.449
    Симпатии:
    0
    Адрес:
    г.Волгодонск Роствской обл.
  18. kostyl

    kostyl Guest

    не понятно, зачем два раза писать одинаковый код php ?
     
  19. Mr.M.I.T.

    Mr.M.I.T. Старожил

    С нами с:
    28 янв 2008
    Сообщения:
    4.586
    Симпатии:
    1
    Адрес:
    у тебя канфетка?
    ты про что?
    если про replcer'ы то недоработка и free версия накладывает свои ограничения =))
     
  20. ShamahN

    ShamahN Активный пользователь

    С нами с:
    10 апр 2007
    Сообщения:
    1.449
    Симпатии:
    0
    Адрес:
    г.Волгодонск Роствской обл.
    Блин. Все бы ничего, но вот как бы теперь заставить его не обращать внимания на конструкции jquery типа ${function()}
    ковычки-то имеют магическое значение :):)
     
  21. Psih

    Psih Активный пользователь
    Команда форума Модератор

    С нами с:
    28 дек 2006
    Сообщения:
    2.678
    Симпатии:
    6
    Адрес:
    Рига, Латвия
    Именно поетому в итоге от таких шаблонизаторов отказываются - больше проблем только из-за этого.
     
  22. Mr.M.I.T.

    Mr.M.I.T. Старожил

    С нами с:
    28 янв 2008
    Сообщения:
    4.586
    Симпатии:
    1
    Адрес:
    у тебя канфетка?
    ShamahN
    ммм
    делать как в смарти, спец теги, менять регулярку в методе parseSys()
     
  23. ShamahN

    ShamahN Активный пользователь

    С нами с:
    10 апр 2007
    Сообщения:
    1.449
    Симпатии:
    0
    Адрес:
    г.Волгодонск Роствской обл.
    Mr.M.I.T.
    я нашел выход: главное писать не так
    [js]{function()}[/js]
    а вот так
    [js]{
    function
    }[/js] :) и все прокатывает.
    !Но компилируемые шаблоны мне оч понравились. Давно говорил, что str_replace сакс, а написать что-нить руки не доходили. Вот сейчас думаю быренько смарти порвать и все будет гут.
    Mr.M.I.T. спасибо, что попростому помог почувствовать прекрасный мир шаблонизаторов :):)
     
  24. Kreker

    Kreker Старожил

    С нами с:
    8 апр 2007
    Сообщения:
    5.433
    Симпатии:
    0
    Не очень хорошо, что выходит из <?php ?> много раз.
    Вообще, я бы советовал полностью заменить конструкции такого вида:
    {for($i=0;$i<count($arr);$i++)}
    на такой:
    <list resourse="arr">
    </list>
    То есть, представление делается ближе к html. Код становится наглядней, не засоряет html, и понятней.
    А чтобы не выходить из интерпретатора, я бы посоветовал забить в массив все html-теги и только при их встрече делать выход. Естественно, что при этом надо учесть кучу нюансов, но тогда твой шаблонизатор поднимется на уровень вверх. ИМХО.
     
  25. Mr.M.I.T.

    Mr.M.I.T. Старожил

    С нами с:
    28 янв 2008
    Сообщения:
    4.586
    Симпатии:
    1
    Адрес:
    у тебя канфетка?
    Kreker
    тут цель другая была, сделать наблонизатор максимально приближенный к синтаксису пыха
    а на счёт тегов была идея сделать спец. теги, и юзать дом для обработки, но эт какой-то xslt получается...