Как стать автором
Обновить

PHP-Матрёшки или Шаблоны для любого сайта из 8 строчек

PHP *
Из песочницы
Я расскажу о своих шаблонах (из восьми строчек), которые придумал в 2003 году и с тех пор они меня не подводили и не требовали каких-либо улучшений.

Суть шаблонов – это принцип матрешки, когда маленькая матрёшка — это часть той что побольше.
На практике это выглядит так: мы указываем имя первого пхп-шаблона, внутри которого прописываем имя вышестоящего шаблона, который, в свою очередь, также может указать на своего пхп-родителя и т.д.

image

Физически это выглядит так. У меня в апаче настроен хост http://start.local с домашней директорией для браузеров /home/start.local/www, где лежит стартовый корневой файл /home/start.local/www/index.php, который содержит в себе всего одну строчку:

<?load::integration('main.tpl');?>


Этот текст надо читать так: класс load вызывает функцию (метод) load::integration(), в которую передает один параметр – строчку 'main.tpl'.

Где, main.tpl – это имя файла первого пхп-шаблона, а функция load::integration() содержит механизм, который позволяет обрабатывать указанный шаблон. А конкретнее, он позволяет нам прописывать в шаблоне main.tpl, другой шаблон, например default.tpl, в который будет вложен результат выполнения mail.tpl. В свою очередь default.tpl, также может указать вышестоящий шаблон (как изображено на рисунке). Если же вышестоящих шаблонов не указано, то функция load::integration() остановит сборку шаблонов и отдаст общий результат выполнения всех шаблонов в браузер.

Далее я расскажу как все работает и дам ссылку скачать код.



Данная схема работает благодаря тому что перед началом обработки каждой странички на сервере (в данном случае стартового файла index.php) мы прописываем для пхп, что прежде чем выполнять какой-либо скрипт, нужно предварительно загрузить указанный нами файл. Делается это в файле /home/start.local/www/.htaccess одной строчкой, вот так:

php_value auto_prepend_file /home/start.local/bin/lib/config.mdl


В данном случае, у меня указан файл config.mdl для удобства. Вот его содержание:


#узнать тип OC: вин или никс
define("OS", getenv("COMSPEC")? ";": ":");

#Задать папку, откуда по-умолчанию будут вставляться файлы
#при использовании подключений пхп-файлов.
#Собственно, использование два раза dirname, говорит о том,
#что по-умолчанию будет использована вышестоящая папка,
#от расположения текущего файла, т.е. это будет папка /home/start.local/bin
ini_set("include_path", ini_get("include_path").OS.dirname(dirname(__FILE__))); 

# подключение нужного файла-класса load.cls
require_once	'lib/load.cls';

# подключение файла-класса create.cls, ниже указано зачем нужен
require_once	'lib/create.cls';

#подключение файла-класса db.cls, для работы с базой данных
#require_once	'lib/db.cls';


Таким образом класс load (/home/start.local/bin/lib/load.cls) и его функция load::integration() с помощью файла config.mdl становятся предварительно загруженными для всех скриптов.

Посмотрим в код класса load:


<?
class load
{
	static	$layout	=	'';
	static	$title	=	'';
	static	$body	=	'';	
	static	$path;
	static	$db;	
	static function integration($maket)
	{
		# необязательная вспомогательная переменная
		self::$path = self::path();
		# необязательный вспомогательный объект
		//self::$db	 = new db();
		
		do
		{
	        $current	=	self::$layout;
	        ob_start();
	        require_once	"tpl/" .$maket;
	        self::$body	=	ob_get_clean();
	        $maket		=	self::$layout;
	        
		} while ($current != self::$layout);
		
		echo	self::$body;
	}
	
	
	static function path($url='')
	{
		$var	=	(!$url)?  dirname(getenv("SCRIPT_NAME")):  $url;
		return	explode("/", trim($var, "/\\") );
	}	
}
?>


Как вы видите, функция load::integration() содержит всего 8 значимых строчек, начиная с do{}. Суть которых сводится к тому, чтобы выполнить код нашего шаблона и сохранить результат выполнения в переменной self::$body. А кроме того, стоит проверка на предмет необходимости повторить операцию, если стартовый шаблон был изменён.

Теперь посмотрим на код нашего шаблона main.tpl
<?
load::$layout	=	'default.tpl';
load::$title	=	'Главная страница';
?>
<h2>Главная страница</h2>
<p>Текст главной страницы</p>


Здесь указана управляющая переменная load::$layout, которая указывает на вышестоящий шаблон. И не обязательная переменная load::$title, которая используется в шаблоне default.tpl

Посмотрим на код шаблона default.tpl
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional">
<html>
<head>
	<title><?=load::$title?></title>
</head>
<body>

<table align="center" width="600">
<tr valign="top">
	<td width="150">
		<?
		if ( !empty(load::$path[0]) )
		{
			?>
			<p><a href="<?=create::file('/', 'main.tpl')?>">Главная</a></p>
			<?
		}
		else 
		{
			?>
			<p>меню:</p>
			<?
		}
		?>
		<p><a href="<?=create::file('/news/', 'page.tpl')?>">Новости</a></p>
		<p><a href="<?=create::file('/cont/', 'page.tpl')?>">Контакты</a></p>
	</td>
	<td>
		<?=load::$body?>
	</td>
</tr>
</table>

</body>
</html>


Как вы можете видеть, это просто хтмл-разметка на протяжении которой расставлен обычный пхп-код. Среди пхп-вставок есть вывод на печать переменной load::$body. Эта переменная содержит в себе результат выполнения кода main.tpl, или любого другого шаблона, который укажет в качестве своего родителя переменная load::$layout.

Важно, что в шаблоне default.tpl нет переменной load::$layout с указанием вышестоящего шаблона, поэтому функция load::integration(), внутри которой и происходит все действо с шаблонами, завершает свой цикл do {} и выводит итоговый результат на печать, т.е. отдает результат выполнения в браузер.

Теперь посмотрим на ссылки в меню нашего сайта которые записаны в default.tpl, например

<a href="<?=create::file('/news/', 'page.tpl')?>">Новости</a>


Здесь в атрибуте href тега a вместо ссылки, указано, что нужно напечатать результат выполнения функции create::file() класса create.

Суть работы этой функции очень проста – создать файл по указанному пути (это первый параметр – '/news/') и записать внутри него вызов первого шаблона (это второй параметр 'page.tpl').

Результатом выполнения функции create::file() будет два действия:
  1. в атрибут href, будет добавлена ссылка, т.е. в браузере такая ссылка примет вид
    <a href="/news/">Новости</a>
  2. на сервере по указанному пути будет создан файл для отображения /home/start.local/www/news/index.php, со следующим содержанием
    <?load::integration('page.tpl');?>


Т.е. по указанному пути будет располагаться стартовый шаблон page.tpl, обработка которого будет аналогична рассмотренному main.tpl.

Про условие if() в шаблоне default.tpl я рассказывать не буду, т.к. это на логику шаблонов не влияет, а относится чисто к дизайну сайта и не более того.

В заключении осталось посмотреть код класса create с функцией create::file(). Код такой:

<?
class create
{
	# Функция создание папок
	static function dir($arr, $dir='')		
	{
		if ($dir=='')	$dir = $_SERVER['DOCUMENT_ROOT'];

		#меняем папку
		chdir($dir);	

		#Перебираем все элементы пути
		foreach ($arr as $a) 
		{
			if ( $a=='' )	continue;
			
			if( !is_dir($a.'/') ){
				mkdir($a, 0770) or print("Ошибка создания каталога $dir/<b>$a</b><br>");
			}
			chdir($a);
			
			$dir	.=	'/' .$a;
		}
		
		return $dir;
	}
	
	# Функция создания файлов по указанному пути
	static function file($path, $template, $content='')
	{
		#Обрезаем слеши с краев
		$path	=	trim($path, '/');
		#делаем массив
		$arr	=	explode('/', $path);
		#забираем последний элемент массива
		$end	=	array_pop($arr);
		#если в конце не файл
		if ( !strpos($end, '.php') )
		{
			#возвращаем последний элемент массива
			array_push($arr, $end);
			$end	=	'index.php';
			
			$temp[ $path ]	=	'/'.$path.'/';
			$temp[ ''	 ]	=	'/';
			$path	=	$temp[ $path ];
		}
		else
		{
			$path = '/'.$path;
		}
				
		$dir = self::dir($arr);		#Создаем нужные папки
		
		
		
		#окрываем для записи файл.
		$fp = fopen($end, "w+") or print ("Ошибка открытия файла $dir/<b>$end</b>!");
		
		#содержание файла
		$content	=	$content==''?	"<?load::integration('$template');?>": $content;
		
		#пишем в файл.
		fputs($fp, $content); 
		
		#закрываем файл.
		fclose ($fp);
		
		return $path;
	}
}
?>


Здесь собственно две функции (метода):
  1. Создание папок, функция create::dir()
  2. Создание файла внутри папки и наполнение файла одной строчкой с указанием какой шаблон нужно использовать для старта, рассмотренная функция create::file()

Вот и все! 8 строчек. По-нагляднее можете скачать архив моего хоста start.local.

Такой техникой я пишу сайты на чистом пхп уже 8 лет и горя не знаю.
Нужен раздел с индивидуальным дизайном? В два счета делаю новый шаблон и подключаю туда другой дизайн.
Нужно добавить форму на сайт? Делаю форму в отдельном шаблоне и подключаю её с помощью механизма шаблонов или обычной require_once вставкой.

Конечно, у меня накопилось не мало разных сопутствующих классов и функций: для работы с базой данных, или формами, или письмами. Но суть в том, что вы можете просто брать и легко использовать как свои наработки, так и подключать сторонние библиотеки к пхп-матрёшкам. Это будет работать благодаря всего лишь 8 строчкам. Без выпиливаний, натягивания и плясок с бубном.

Надеюсь мой опыт пригодиться и сослужит хорошую службу. Например, в ситуации когда, вам необходимо доказать, что вы это сделать можете, но в рамках какой-то сиэмэс просто не знаете как этого добиться.

PS.
В следующей статье я расскажу, что для управления базой данных сайта можно использовать просто phpMyAdmin, который умеет работать со связанными таблицами, т.е. вы не просто меняете айдишник в связанном поле, но вы одновременно можете видеть полноценные связанные данные из связанных таблиц, которые стоят за этим айдишником.
Теги:
Хабы:
Всего голосов 52: ↑16 и ↓36 -20
Просмотры 10K
Комментарии 31
Комментарии Комментарии 31

Публикации

Истории

Работа

PHP программист
236 вакансий