[ Поиск ] - [ Пользователи ] - [ Календарь ]
Полная Версия: Класс для построения шаблонов
Страницы: 1, 2, 3, 4
McLotos
Добрый день! :lol:
Как и обещал, выкладываю сюда класс для построения шаблонов. Его описание и некоторые примеры использования лежат в песочнице хабра.
А тут я расскажу более подробно о технической стороне класса. Опишу его свойства методы и похвастаюсь какой он хороший и как он упростил создание моего последнего сайта.
И так
код класса

public $ext; # расширение файлов шаблона
public $htm; # расширение файлов сохраненных страниц
public $skin_dir; # директория для поиска шаблона
public $js_dir; # директория для поиска js-файлов
public $css_dir; # директория для поиска css-файлов
public $img_dir; # директория для поиска картинок
public $skin; # имя шаблона
public $cache; # директория для сохраненных страниц
public $log; # имя файла логов
public $tag_start; # тэг начала переменной
public $tag_end; # тэг конца переменной
public $dir_delimeter; # символ разделителя директорий
public $space; # символ пробела
public $cacheFileName; # имя файла сохраненной страницы

private $field; # сюда пишем имя поля для методов сортировки
private $buffer = array(); # здесь сохраняется предварительный код шаблона
private $vars = array(); # здесь будут все переменные
private $error_log = array(); # сюда записываем ошибки
private $dom; # здесь у нас хранится объект
private $sysMsg; # тут будут системные сообщения
# проверяет все ли свойства объекта переданы

public function checkVariables()
{
$this -> sysMsg = empty($this -> ext) === FALSE ? '' : 'Передайте в класс переменную ext с указанием расширения файлов шаблона'."\n\r";
$this -> sysMsg .= empty($this -> htm) === FALSE ? '' : 'Передайте в класс переменную htm с указанием расширения файлов кэша'."\n\r";
$this -> sysMsg .= empty($this -> skin_dir) === FALSE ? '' : 'Передайте в класс переменную skin_dir с указанием каталога шаблонов'."\n\r";
$this -> sysMsg .= empty($this -> js_dir) === FALSE ? '' : 'Передайте в класс переменную js_dir с указанием каталога js файлов'."\n\r";
$this -> sysMsg .= empty($this -> css_dir) === FALSE ? '' : 'Передайте в класс переменную css_dir с указанием каталога главного css файла'."\n\r";
$this -> sysMsg .= empty($this -> img_dir) === FALSE ? '' : 'Передайте в класс переменную img_dir с указанием каталога картинок шаблона'."\n\r";
$this -> sysMsg .= empty($this -> skin) === FALSE ? '' : 'Передайте в класс переменную skin с указанием имени шаблона который нужно использовать'."\n\r";
$this -> sysMsg .= empty($this -> cache) === FALSE ? '' : 'Передайте в класс переменную cache с указанием адреса каталога для кэша шаблонов'."\n\r";
$this -> sysMsg .= empty($this -> log) === FALSE ? '' : 'Передайте в класс переменную log с указанием адреса и имени файла для вывода ошибок'."\n\r";
$this -> sysMsg .= empty($this -> tag_start) === FALSE ? '' : 'Передайте в класс переменную tag_start чтобы обозначить символ который считается началом тэгов'."\n\r";
$this -> sysMsg .= empty($this -> tag_end) === FALSE ? '' : 'Передайте в класс переменную tag_end чтобы обозначить символ который считается концом тэгов'."\n\r";
$this -> sysMsg .= empty($this -> dir_delimeter) === FALSE ? '' : 'Передайте в класс переменную dir_delimeter чтобы обозначить символ который считается разделителем каталогов'."\n\r";
$this -> sysMsg .= empty($this -> space) === FALSE ? '' : 'Передайте в класс переменную space чтобы обозначить символ который считается пробелом'."\n\r";
if(empty($this -> sysMsg) === FALSE)
{die($this -> sysMsg);}
}


# проверяет создан ли кэш запрашиваемой страницы
public function TestPageStatus()
{
$this -> CheckVariables();
$result = is_file($this -> CurrentPage());
return $result;
}

# получает адрес текущей страницы и отдает предполагаемое имя
private function CurrentPage()
{
$page = (empty($_SERVER['PATH_INFO']) === FALSE) ? str_replace($this -> dir_delimeter,$this -> space,$_SERVER['PATH_INFO']) : str_replace($this -> dir_delimeter,$this -> space,'HomePage');
$this -> cacheFileName = $this -> cache . $_SESSION['lang'] . $this -> dir_delimeter.md5($page) . $this -> htm;
return $this -> cacheFileName;
}

# публичная версия метода buffer
public function page()
{
$files = func_get_args();
if(empty($files) === FALSE)
{$this -> buffer($files);}
}


# создает массив buffer, в котором сохраняет строки файлов шаблонов
# например если будет передано несколько файлов шаблона, то он встроит их в body в том порядке, в каком они переданы

public function buffer($filename)
{
if(empty($this -> buffer) === TRUE)
{
if(is_file($this -> skin_dir . $this -> skin . 'header' . $this -> ext) === TRUE)
{
$header = file($this -> skin_dir . $this -> skin . 'header' . $this -> ext);
$this -> buffer = array_merge($this -> buffer,$header);
}
else
{
$this -> addErrorInLog('Не найден header шаблона');
}
for($i=0;$i<count($filename);$i++)
{
$page = file($this -> skin_dir . $this -> skin . $filename[$i] . $this -> ext);
$this -> buffer = array_merge($this -> buffer, $page);
}
$this -> buffer = array_diff($this -> buffer,array(''));
$this -> buffer = array_values($this -> buffer);
}
elseif(empty($this -> buffer) === FALSE)
{
$tmpObj = new DOMDocument;
for($i=0;$i<count($filename);$i++)
{
$tmpObj -> load($this -> skin_dir . $this -> skin . $filename[$i] . $this -> ext);
$this -> dom -> getElementsByTagName('body') -> item(0) -> appendChild($this -> dom -> importNode($tmpObj -> childNodes -> item(0), true));
}
}
}


# ищет файлы по расширению в указанном каталоге
public function FindFiles($where,$what)
{
$script = array();
if (substr($_SERVER['SERVER_NAME'], 0, 4)=='www.')
{
$_SERVER['SERVER_NAME'] = substr($_SERVER['SERVER_NAME'], 4);
}
$handle = opendir($where);
while (false !== ($file = readdir($handle)))
{
if ($file != "." && $file != ".." && (!is_dir($where.$file)))
{$script[count($script)]='http://'.$_SERVER['SERVER_NAME'].'/'.$where.$file;}
}


sort($script);
return $script;
}

# получает имя ключа и массив значений, создает переменные для поиска в шаблоне
public function Assign($var,$arr)
{
foreach($arr as $key => $val)
{
if(is_numeric($key) === FALSE)
{
$this -> vars[$this -> tag_start . $var . '.' . ucwords($key) . $this -> tag_end] = $val;
}
else
{
for($i=0;$i<count($arr[$key]);$i++)
{
foreach($arr[$key] as $k => $v)
{
$this -> vars [$this -> tag_start . $var . '.' . ucwords($k) . $this -> tag_end][$key] = $v;
}
}
}
}
}


# публичная версия метода создания страницы
public function build()
{
$this -> createPageObject();
}

private function createPageObject()
{
$this -> dom = new DOMDocument;
libxml_use_internal_errors(true);
libxml_clear_errors();
$this -> buffer = implode($this -> buffer);
$this -> dom -> substituteEntities = TRUE;
$this -> dom -> validateOnParse = true;
$this -> dom -> formatOutput = true;
$this -> dom -> encode = 'UTF-8';
$this -> dom -> loadHTML($this -> buffer);
$this -> findRequired();
$this -> dom -> preserveWhiteSpace = true;
}

# ищет тэги filename и заменяет их на файлы
private function findRequired()
{$tmpObj = new DOMDocument;
$required = $this -> dom -> getElementsByTagName('filename');
for($i=0;$i<$required -> length;$i++)
{
$tmpObj -> load($this -> skin_dir . $this -> skin . $required -> item($i) -> nodeValue . $this -> ext);
$required -> item($i) -> parentNode -> appendChild($this -> dom -> importNode($tmpObj -> childNodes -> item(0), true));
$required -> item($i) -> parentNode -> removeChild($required -> item($i));
}
}


# выводит получившуюся страницу если она есть, а если нету то вызывает методы для ее создания
public function ShowPage()
{
$args = func_get_args();

if($this -> TestPageStatus() === TRUE)
{
require $this -> cache . $_SESSION['lang'] . $this -> dir_delimeter . $this -> CurrentPage() . $this -> htm;
}
else
{
$this -> prepareHead();
$this -> prepareBody();
$final = htmlspecialchars_decode($this -> dom -> saveHTML());
$this -> dom -> loadHTML($final);
$this -> dom -> formatOutput = TRUE;
$this -> dom -> substituteEntities = TRUE;
$this -> dom -> validateOnParse = true;
$this -> dom -> formatOutput = true;
$this -> dom -> encode = 'UTF-8';
$this -> dom -> saveHTMLFile($this -> CurrentPage());
require $this -> CurrentPage();
if(empty($args[0]) === FALSE)
{
unlink($this -> CurrentPage());
}
}
}


# для таких массивов типа многоуровневое меню используется свой способ сортировки через cmp
public function sort(array &$array, $field)
{
$this -> field = $field;
usort($array, array('Template', 'cmp'));
}

private function cmp($a, $b)
{
$x = $a[$this -> field];
$y = $b[$this -> field];
return $x > $y ? 1 : ($x < $y ? -1 : 0);
}

# CreateChild создает любой элемент в любой части шаблона
# !!! Работает только в связке с методами поиска

public function createChild()
{
$args = func_get_args();
$result = $args[0] -> appendChild($this -> dom -> createElement($args[1]));
if(empty($args[2]) === FALSE)
{
if(strpos($args[2],'=') === FALSE)
{$result -> nodeValue = $args[2];}
else
{
$attrs = explode(',',$args[2]);
for($k=0;$k<count($attrs);$k++)
{
$attrexpl=explode('=',trim($attrs[$k]));
if(empty($attrexpl[1]) === FALSE)
{
$attrname = $attrexpl[0];
$attrvalue = $attrexpl[1];
}
$result -> appendChild($this -> dom -> createAttribute($attrname));
$result -> setAttribute($attrname,trim(trim($attrvalue,"'"),'"'));
}
}
}

if(empty($args[3]) === FALSE)
{
$result -> nodeValue = $args[3];
}
return $result;
}

# поиск элемента по его id
public function findById($id)
{return $this -> dom -> getElementById($id);}

# поиск первого элемента по указанному тэгу
public function findByTagName($tag)
{
$list = $this -> dom -> getElementsByTagName($tag);
return $list -> item(0);
}

# заменяет в шаблоне языковые переменные на соответствующее языковое значение,
public function Translate($str,$arr)
{
foreach($arr as $k=>$v)
{$str = str_replace($k, $v,$str);}
return $str;
}

# распаковывает некоторые BBкоды
public function unpackBB($string)
{
preg_match('#\['.$_SESSION['lang'].']*\](.+)\[/'.$_SESSION['lang'].'\]#U', $string, $m);

$str_search = array('#\\n#is', '#\[p\](.+?)\[\/p\]#is', '#\[table\](.+?)\[\/table\]#is', '#\[thead\](.+?)\[\/thead\]#is',
'#\[tbody\](.+?)\[\/tbody\]#is', '#\[tr\](.+?)\[\/tr\]#is', '#\[td rowspan=(.+?)\](.+?)\[\/td\]#is', '#\[td\](.+?)\[\/td\]#is',
'#\[td colspan=(.+?)\](.+?)\[\/td\]#is', '#\[table id=(.+?)\](.+?)\[\/table\]#is', '#\[h1\](.+?)\[\/h1\]#is', '#\[h3\](.+?)\[\/h3\]#is',
'#\[b\](.+?)\[\/b\]#is', '#\[i\](.+?)\[\/i\]#is', '#\[u\](.+?)\[\/u\]#is', '#\[code\](.+?)\[\/code\]#is', '#\[url=(.+?)\](.+?)\[\/url\]#is',
'#\[url\](.+?)\[\/url\]#is', '#\[img\](.+?)\[\/img\]#is', '#\[size=(.+?)\](.+?)\[\/size\]#is', '#\[color=(.+?)\](.+?)\[\/color\]#is',
'#\[list\](.+?)\[\/list\]#is', '#\[listn](.+?)\[\/listn\]#is', '#\[liste](.+?)\[\/liste\]#is', '#\[\*\](.+?)\[\/\*\]#');

$str_replace = array('<br />', '<p>\\1</p>', '<table>\\1</table>', '<thead>\\1</thead>', '<tbody>\\1</tbody>', '<tr>\\1</tr>',
'<td rowspan="\\1">\\2</td>', '<td>\\1</td>', '<td colspan="\\1">\\2</td>', '<table id="\\1">\\2</table>', '<h1>\\1</h1>',
'<h3>\\1</h3>', '<b>\\1</b>', '<i>\\1</i>', '<span style="text-decoration:underline">\\1</span>', '<code class="code">\\1</code>',
'<a href="\\1">\\2</a>', '<a href="\\1">\\1</a>', '<img src="\\1" alt = "Изображение" />', '<span style="font-size:\\1%">\\2</span>',
'<span style="color:\\1">\\2</span>', '<ul>\\1</ul>', '<ol>\\1</ol>', '<li>\\1</li>');
$result = preg_replace($str_search, $str_replace, $m[1]);
$result = mb_convert_encoding($result, 'HTML-ENTITIES', 'utf-8');
$content = new DOMDocument();
$content -> formatOutput = true;
$content -> encoding = 'UTF-8';
$content -> loadHTML($result);
$content -> doctype -> parentNode -> removeChild($content -> doctype);
$html = $content -> getElementsByTagName('html') -> item(0);
$fragment = $content -> createDocumentFragment();
while ($html -> childNodes -> length > 0)
{$fragment -> appendChild($html -> childNodes -> item(0));}

$html -> parentNode -> replaceChild($fragment, $html);
$body = $content -> getElementsByTagName('body') -> item(0);
$fragment = $content -> createDocumentFragment();
while ($body -> childNodes -> length > 0)
{$fragment->appendChild($body->childNodes->item(0));}
$body -> parentNode -> replaceChild($fragment, $body);
return $content -> SaveHTML();
}

# метод подготавливает секцию head шаблона
private function prepareHead()
{
$HeadSection = $this -> dom -> getElementsByTagName('head') -> item(0);
for($i=0;$i<$HeadSection -> childNodes -> length;$i++)
{
if($HeadSection -> childNodes -> item($i) -> hasAttributes() === TRUE)
{
foreach ($HeadSection -> childNodes -> item($i) -> attributes as $attrs)
{
if(array_key_exists($attrs -> nodeValue, $this -> vars) === TRUE)
{
if(is_array($this -> vars[$attrs -> nodeValue]) === FALSE)
{
$HeadSection -> childNodes -> item($i) -> setAttribute($attrs -> nodeName,$this -> vars[$attrs -> nodeValue]);
}
else
{
for($j=0;$j<count($this -> vars[$attrs -> nodeValue]);$j++)
{$elem = $HeadSection -> childNodes -> item($i) -> cloneNode(false);
$elem -> setAttribute($attrs -> nodeName,$this -> vars[$attrs -> nodeValue][$j]);
$HeadSection -> appendChild($elem);}
$HeadSection -> childNodes -> item($i) -> parentNode -> removeChild($HeadSection -> childNodes -> item($i));
}
}
}
}

if(array_key_exists($HeadSection -> childNodes -> item($i) -> nodeValue,$this -> vars) === TRUE)
{
$HeadSection -> childNodes -> item($i) -> nodeValue = $this -> vars[trim($HeadSection -> childNodes -> item($i) -> nodeValue)];
}
}

for($i=0;$i<$HeadSection -> childNodes -> length;$i++)
{
if(($HeadSection -> childNodes -> item($i) -> nodeName == 'script') && (empty($HeadSection -> childNodes -> item($i) -> nodeValue) === FALSE))
{$HeadSection -> appendChild($HeadSection -> childNodes -> item($i));}
}
}


# метод подготавливает body
private function prepareBody()
{ $BodySection = $this -> dom -> getElementsByTagName('body') -> item(0);
for($i=0;$i<$BodySection -> childNodes -> length;$i++)
{
$this -> checkAndCopy($BodySection -> childNodes -> item($i));
}
}


# проверяем нужно ли копировать блоки или элементы шаблона
private function checkAndCopy($elem)
{
$this -> setValues($elem);
if(($elem -> nodeName !== '#text') && ($elem -> nodeName !== '#cdata-section'))
{
if(array_key_exists($elem -> getAttribute('id'), $this -> vars) === TRUE)
{
if(is_array($this -> vars[$elem -> getAttribute('id')]) === TRUE)
{
for($a=0;$a<count($this -> vars[$elem -> getAttribute('id')]);$a++)
{
$new = $elem -> parentNode -> appendChild($elem -> cloneNode(TRUE));
$this -> setValues($new,$a);
$this -> setAttributes($new,$a);
}
$elem -> parentNode -> removeChild($elem -> parentNode -> childNodes -> item(0));
}
}

for($i=0;$i<$elem -> childNodes -> length;$i++)
{
$this -> checkAndCopy($elem -> childNodes -> item($i));
}
}


}


# устанавливаем значения для всех переменных найденных в шаблоне
private function setValues()
{
$args = func_get_args();
if($args[0] -> nodeName == '#text')
{
if((array_key_exists(trim($args[0] -> nodeValue),$this -> vars) === TRUE) && (is_array($this -> vars[trim($args[0] -> nodeValue)]) === FALSE))
{$args[0] -> nodeValue = $this -> vars[trim($args[0] -> nodeValue)];}
}

if(($args[0] -> nodeName !== '#text') && ($args[0] -> nodeName !== '#cdata-section'))
{
for($c=0;$c<$args[0] -> childNodes -> length;$c++)
{
if(array_key_exists($args[0] -> childNodes -> item($c) -> nodeValue, $this -> vars) === TRUE)
{
if((is_array($this -> vars[urldecode($args[0] -> childNodes -> item($c) -> nodeValue)]) === TRUE) && (isset($args[1]) === TRUE))
{
$args[0] -> childNodes -> item($c) -> nodeValue = $this -> vars[urldecode($args[0] -> childNodes -> item($c) -> nodeValue)][$args[1]];
}
elseif(is_array($this -> vars[urldecode($args[0] -> childNodes -> item($c) -> nodeValue)]) === FALSE)
{
$args[0] -> childNodes -> item($c) -> nodeValue = $this -> vars[urldecode($args[0] -> childNodes -> item($c) -> nodeValue)];
}
}
}
}
}


# устанавливаем значения атрибутов тэгов шаблона
private function setAttributes($elem,$pos)
{
if($elem -> hasAttributes() === TRUE)
{
foreach ($elem -> attributes as $attr)
{
if(array_key_exists($attr -> nodeValue, $this -> vars) === TRUE)
{
if(is_array($this -> vars[urldecode($attr -> nodeValue)]) === TRUE)
{
$elem -> setAttribute($attr -> nodeName, $this -> vars[urldecode($attr -> nodeValue)][$pos]);
}
elseif(is_array($this -> vars[urldecode($attr -> nodeValue)]) === FALSE)
{
$elem -> setAttribute($attr -> nodeName, $this -> vars[urldecode($attr -> nodeValue)]);
}
}
}

for($i=0;$i<$elem -> childNodes -> length;$i++)
{
$this -> setAttributes($elem -> childNodes -> item($i),$pos);
}
}
}


# сохраняем ошибки в файл
private function saveErrorReports()
{
foreach($this -> error_log as $val)
{file_put_contents($this -> log, '['.date('d.m.Y H:i:s').'] '.$val."\n\r", FILE_APPEND | LOCK_EX);}
}


# добавляем ошибку в список
private function addErrorInLog($id)
{
$this -> error_log[count($this -> error_log)] = $id;
if(empty($this -> error_log) === FALSE)
{
$this -> saveErrorReports();
$this -> showErrorReports();
}
}


# выводим ошибки из файла
private function showErrorReports()
{
require $this -> log;
unlink($this -> log);
die();
}
}


Ну вот. Жду злостные комменты на тему какой ужасный велосипед с квадратными колесами

_____________
программирование - инструмент для решения конкретных задач, любая попытка спроектировать что-то универсальное приведет к провалу.©paul85
В любом случае тебе прийдётся пройти путь изобретения велосипеда, который прошли другие, только причиной твоего изобретения будет непонимание принципов работы велосипеда изобретённого другими людьми.©SlavaFr
jQuery это попытка использовать АН-225 для перевозки зубочистки
Быстрый ответ:

 Графические смайлики |  Показывать подпись
Здесь расположена полная версия этой страницы.
Invision Power Board © 2001-2024 Invision Power Services, Inc.