Цитата (McLotos @ 25.12.2014 - 09:43) | ||
А я не увидел особого смысла в нем. Например в классе для работы с БД я убрал конструктор чтобы можно было менять параметры на лету, не создавая новый экземпляр класса. Решил что и здесь это будет удобно. Хотя не знаю, может и добавлю в следующей версии |
Цитата (twin @ 25.12.2014 - 11:54) |
Все равно сначала придется сверстать шаблон, посмотреть как он выглядит, а потом удалить все теги, перенеся их генерацию на сторону PHP. |
<table>
<tr>
<td>{CONTENT.Col1}</td>
<td>{CONTENT.Col2}</td>
<td>{CONTENT.Col3}</td>
</tr>
</table>
<table>
<tr><td>1.1</td><td>1.2</td><td>1.3</td></tr>
<tr><td>2.1</td><td>2.2</td><td>2.3</td></tr>
</table>
Цитата (twin @ 25.12.2014 - 10:06) |
volter9 А причем тут ООП вообще? Это просто класс, не более того. Реализация конечно не ахти, как по стилистике, так и по отдельным ляпам. Но это дело поправимое. |
Цитата (McLotos @ 25.12.2014 - 10:34) |
volter9 предлагаешь какие-то методы убрать в расширения для класса? или вообще в самостоятельные функции? |
Цитата (twin @ 25.12.2014 - 10:44) |
volter9 Ну то, что все свалено в кучу, это понятно. Но ООП тут не причем, это можно и на статике реализовать. А так то да, класс занимается совершенно непрофильными задачами. Только разбивкой на отдельные классы эту проблему не решить. Все равно god object получится. Просто дискретный. ![]() |
<?php
class Template
{
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();
}
}