Статьи

Сверните свою собственную систему шаблонов в PHP

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


Краткий ответ? Вы не Так зачем? Из глубины моего отвратительного маленького сердца я считаю, что все разработчики должны постоянно стремиться изучать новые и / или сложные концепции. Это то, что делает нас умнее, делает нашу работу интересной и делает нашу повседневную работу менее трудоемкой (потому что, вы знаете, мы становимся намного умнее). С этой целью развертывание вашей собственной системы шаблонов дает вам возможность оттачивать свои навыки работы с PHP с помощью изнурительных концепций, таких как карри и регулярные выражения . И, эй! Вы можете обнаружить, что можете использовать систему шаблонов в одном из ваших будущих проектов.


«Все разработчики должны постоянно подталкивать себя к изучению новых и / или сложных концепций».

Прежде чем мы просто погрузимся и начнем программировать, давайте выясним, что именно мы пытаемся сделать. Создавая эту систему шаблонов, мы надеемся:

  1. Почти полностью отделить HTML-разметку от наших PHP-скриптов
  2. Сделайте данные из наших серверных сценариев более доступными для дизайнеров
  3. Упростите обслуживание, абстрагируя логику отображения от бизнес-логики

Система шаблонов, которую мы собираемся построить в этом упражнении, будет состоять из одного класса Template , содержащего два свойства и пять методов, и папки, содержащей шаблоны для анализа. Кроме этого, нам просто нужен файл для вывода некоторых тестовых данных. Звучит довольно просто, правда?

Чтобы дизайнеру было действительно легко форматировать данные, поступающие из наших серверных сценариев, мы собираемся создать систему, которая позволяет использовать шаблон с необязательным верхним и нижним колонтитулами с циклом, который будет использоваться для форматирования. Записи возвращаются из скрипта. Если мы сократим его до самого основного состояния, эта система пройдет через следующие шаги:

  1. Загрузка записей для анализа в виде массива объектов
  2. Загрузите файл шаблона для использования
  3. Отделите верхний и нижний колонтитулы от цикла
  4. Найдите теги шаблона с помощью регулярных выражений
  5. Проверьте, соответствует ли тег шаблона свойству в объекте записи
  6. Замените тег шаблона данными соответствующего свойства

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

Сначала создайте папку для вашего проекта. Я назвал мои шаблоны . Внутри папки проекта создайте две новые папки: активы и система . Папка активов будет содержать шаблоны для нашей демонстрации. Системная папка будет содержать класс Template . Внутри папки активов создайте две новые папки: шаблоны . Угадай, что он держит!

Затем создайте index.php в главной папке проекта. Здесь мы протестируем систему шаблонов, как только она будет готова к работе, и мы будем использовать ее, чтобы убедиться, что наши отдельные шаги также работают на этом пути. Однако сейчас вы можете оставить его пустым. В системной папке создайте новый файл PHP с именем class.template.inc.php . Внутри определите класс, который мы назовем Template , и давайте сделаем наши шаги сверху, чтобы создать список дел:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
<?php
  
/** * A templating engine * * PHP version 5 * * LICENSE: This source file is subject to the MIT License, available at * http://www.opensource.org/licenses/mit-license.html * * @author Jason
Lengstorf <[email protected]> * @copyright 2010 Copter Labs * @license http://www.opensource.org/licenses/mit-license.html MIT License */ class Template {
  
 //TODO: Define a class property to store the template
  
 //TODO: Define a class property to store the entries
  
 //TODO: Write a public method to output the result of the templating engine
  
 //TODO: Write a private method to load the template
  
 //TODO: Write a private method to parse the template
  
 //TODO: Write a static method to replace the template tags with entry data
  
 //TODO: Write a private currying function to facilitate tag replacement
  
}

ПРИМЕЧАНИЕ. Тот факт, что метод замены тега является статическим, и что функция каррирования присутствует вообще, — это то, чему вы должны доверять мне сейчас. Я все объясню, если ты останешься со мной.


Прежде чем мы начнем разбирать шаблоны, давайте решим, как будут выглядеть наши шаблоны. Шаблон должен быть максимально простым в использовании. Если мы сделаем это правильно, любой полукомпетентный пользователь HTML сможет легко создавать шаблоны. Это означает, что наш шаблон должен быть максимально приближен к написанию HTML.

Чтобы определить наш шаблон, давайте начнем с простого макета записи, как мы могли бы видеть на веб-странице:

01
02
03
04
05
06
07
08
09
10
<h3>Other Articles to Check Out</h3>
  
Here are some other articles on the Envato network.
  
<ul id=»entries»>
  
 <li> <h4><a href=»http://net.tutsplus.com/»>Some Article</a></h4> <p class=»byline»>Published on net.tutsplus.com <p class=»read-more»><a href=»http://net.tutsplus.com/»>read full article
&raquo;</a> </li>
  
</ul><!— end #entries —>

Отлично. Это достаточно просто. Итак, давайте выделим части, которые будут варьироваться от статьи к статье:

  • URL — URL статьи
  • Заголовок — название статьи
  • Издатель — сайт, на котором опубликована статья

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

1
2
3
4
5
6
7
8
9
<h3>Other Articles to Check Out</h3>
  
Here are some other articles on the Envato network.
  
<ul id=»entries»>
  
 <li> <h4><a href=»url»>title</a></h4> <p class=»byline»>Published on site <p class=»read-more»><a href=»url»>read full article &raquo;</a> </li>
  
</ul><!— end #entries —>

Но мы не можем просто использовать слово «заголовок» в качестве тега шаблона для заголовка; Что делать, если кто-то использует слово «заголовок» в ее разметке? Это привело бы к тому, что каждое вхождение слова заменялось заголовком записи — очевидно, это не правильное поведение. Чтобы избежать этого, нам нужно обернуть теги нашего шаблона в то, что вряд ли появится в тексте. Для этого примера мы будем использовать фигурные скобки ( {} ).

1
2
3
4
5
6
7
8
9
<h3>Other Articles to Check Out</h3>
  
Here are some other articles on the Envato network.
  
<ul id=»entries»>
  
 <li> <h4><a href=»{url}»>{title}</a></h4> <p class=»byline»>Published on {site} <p class=»read-more»><a href=»{url}»>read full article &raquo;</a> </li>
  
</ul><!— end #entries —>

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


Поскольку я хотел бы попасть прямо во внутренности движка шаблонов, мы не собираемся тратить много времени на сами записи. Я собираюсь использовать API Envato и реализовать шаги для href = «http://net.tutsplus.com/tutorials/php/display-anything-you-want-from-the-envato-api-using-php/»> используя API Envato от Дрю Дугласса . Откройте index.php в корне папки вашего проекта и вставьте следующий код.

01
02
03
04
05
06
07
08
09
10
11
12
13
<?php
  
/** * Loads entries from the Envato API for a given site * * @link https://marketplace.envato.com/api/documentation * * @param string $site The site from which entries should be loaded * @return array An array of objects containing entry data */ function load_envato_blog_posts( $site=&#39;themeforest&#39; ) { // Set up the request for the Envato API $url = &#39;http://marketplace.envato.com/api/edge/blog-posts:&#39;.$site.&#39;.json&#39;;
  
 // Initialize an empty array to store entries $entries = array();
  
 // Load the data from the API $ch = curl_init();
curl_exec($ch);
  
 // If entries were returned, load them into the array if(!empty($ch_data)) { // Convert the JSON into an array of entry objects $json_data = json_decode($ch_data, TRUE);
$json_data[&#39;blog-posts&#39;] as $entry ) { $entries[] = (object) $entry;
  
 return $entries;

Как вы можете видеть в комментариях, мы используем cURL для отправки запроса в API Envato и сохраняем возвращенные данные в $ ch_data . Затем, предполагая, что записи были возвращены, эти записи преобразуются из возвращенного формата JSON в массив объектов. ПРИМЕЧАНИЕ. Для получения дополнительной информации об объектно-ориентированном PHP ознакомьтесь со статьей Nettuts об объектно-ориентированном программировании на PHP , моей книгой Pro PHP и jQuery или с кратким описанием ООП в Википедии. Объекты ввода содержат свойства, которые мы можем использовать в наших шаблонах. Чтобы увидеть эти данные, добавьте жирный код ниже в index.php :

1
2
3
4
5
<?php
  
echo ‘<pre>’, print_r(load_envato_blog_posts(), TRUE), ‘</pre>’;
  
/** * Loads entries from the Envato API for a given site * … */ function load_envato_blog_posts( $site=&#39;themeforest&#39; ) {…}

ПРИМЕЧАНИЕ. Код приведен для краткости. Если вы загрузите index.php в своем браузере, вы увидите нечто похожее на следующее:

1
2
3
4
5
6
7
Array ( [0] => stdClass Object ( [title] => Interview With «The Man»: Jeffrey Way [url] => http://feedproxy.google.com/~r/themeforest/~3/5oZEgpMCn3Q/ [site] => themeforest.net [posted_at] => 2009-12-19 )
  
 [1] => stdClass Object ( [title] => ThemeForest Week in Review [url] => http://feedproxy.google.com/~r/themeforest/~3/fAiw8Xw1Q8U/ [site] => themeforest.net [posted_at] => 2009-12-19 )
  
 …more entries…
  
)

Свойства класса — $ title , $ url , $ site и $ posts_at — будут соответствовать тегам шаблона {title} , {url} , {site} и {posts_at} . Мы узнаем, как это будет работать чуть позже.

Возможно, вы заметили, что записи не загружаются и не хранятся в классе Template прямо сейчас. Это потому, что мы хотим, чтобы наша система шаблонов была совместима с любым набором записей, и довольно легко взять любой набор записей из базы данных или веб-службы и организовать их в массив объектов. Однако нам нужно хранить записи в системе шаблонов, чтобы их можно было проанализировать. Чтобы это было просто и понятно , просто откройте class.template.inc.php и создайте открытое свойство с именем $ records . Это будет хранить записи для разбора позже.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
<?php
  
/** * A templating engine * … */ class Template {
  
 //TODO: Define a class property to store the template
  
 /** * Stores the entries to be parsed in the template * @var array */ public $entries = array();
  
 //TODO: Write a public method to output the result of the templating engine
  
 //TODO: Write a private method to load the template
  
 //TODO: Write a private method to parse the template
  
 //TODO: Write a static method to replace the template tags with entry data
  
 //TODO: Write a private currying function to facilitate tag replacement
  
}

Наш следующий шаг — создать метод, который будет загружать файл шаблона для разбора. Поскольку этот метод должен выполняться только как часть всеобъемлющего процесса создания шаблонов, мы можем сделать этот метод закрытым. В интересах соблюдения следующих соглашений об именах PEAR мы будем вызывать этот метод _load_template () . Кроме того, нам нужны два новых свойства для хранения пути к файлу шаблона и загруженного шаблона, которые мы будем называть $ template_file и $ _template соответственно.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
<?php
  
/** * A templating engine * … */ class Template {
  
 /** * Stores the location of the template file * @var string */ public $template_file,
  
 /** * Stores the entries to be parsed in the template * @var array */ $entries = array();
  
 /** * Stores the contents of the template file * @var string */ private $_template;
  
 //TODO: Write a public method to output the result of the templating engine
  
 /** * Loads a template file with which markup should be formatted * * @return string The contents of the template file */ private function _load_template( ) { // Load the template… }
  
 //TODO: Write a private method to parse the template
  
 //TODO: Write a static method to replace the template tags with entry data
  
 //TODO: Write a private currying function to facilitate tag replacement
  
}

ПРИМЕЧАНИЕ. Не забудьте настроить свойство $ records для добавления $ template .

Наш первый шаг в загрузке шаблона — убедиться, что файл существует, прежде чем пытаться его открыть. Это помогает избежать ошибок, и действительно, это просто хорошая идея, чтобы быть уверенным. Мы сделаем это с помощью file_exists () . Кроме того, поскольку существует внешняя вероятность того, что права доступа к файлу могут не позволять нам читать его содержимое, мы также должны проверить это с помощью is_readable () . Поскольку мы пытаемся сделать это как можно более простым, мы добавим шаблон по умолчанию на случай, если предоставленный шаблон не существует или по какой-либо причине не загружается должным образом. После того, как мы выяснили расположение файла шаблона, мы можем загрузить его в наше личное свойство $ _template с помощью file_get_contents () . В class.template.inc.php добавьте следующий жирный код в _load_template () для загрузки нашего шаблона (или по умолчанию):

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<?php
  
/** * A templating engine * … */ class Template {
  
 /** * Stores the location of the template file * @var string */ public $template,
  
 /** * Stores the entries to be parsed in the template * @var array */ $entries = array();
  
 /** * Stores the contents of the template file * @var string */ private $_template;
  
 //TODO: Write a public method to output the result of the templating engine
  
 /** * Loads a template file with which markup should be formatted * … */ private function _load_template( ) { // Check for a custom template $template_file = &#39;assets/templates/&#39;
$this->template_file;
  
 // Look for a system template else if( file_exists($default_file = &#39;assets/templates/default.inc&#39;) && is_readable($default_file) ) { $path = $default_file;
  
 // If the default template is missing, throw an error else { throw new Exception( &#39;No default template found&#39; );
  
 // Load the contents of the file and return them $this->_template = file_get_contents($path);
  
 //TODO: Write a private method to parse the template
  
 //TODO: Write a static method to replace the template tags with entry data
  
 //TODO: Write a private currying function to facilitate tag replacement
  
}

Чтобы разобрать наш шаблон, нам нужно спланировать список шагов, которые будут выполнены для правильной обработки данных:

  1. Удалите все теги в стиле PHP из шаблона
  2. Извлеките основной цикл ввода из файла
  3. Определите регулярные выражения для соответствия любому тегу шаблона
  4. Карри функция, которая заменит теги с входными данными
  5. Извлеките заголовок и обработайте теги шаблона, если они существуют
  6. Извлеките нижний колонтитул и обработайте теги шаблона, если они существуют
  7. Обработайте каждую запись и вставьте ее значения в цикл
  8. Вернуть отформатированные записи с верхним и нижним колонтитулом

С нашим загруженным шаблоном и планом атаки мы можем начать процесс разбора. Это немного сложнее, потому что мы будем вдаваться в регулярные выражения, которые могут быть пугающими и опьяняющими для разработчиков. Итак, прежде чем сойти с ума, давайте немного. Сделайте глубокий вдох и повторите за мной: «С большой силой приходит большая ответственность. Я буду использовать регулярные выражения только тогда, когда нет более простого способа достичь желаемого результата. Поскольку каждый раз, когда я злоупотребляю регулярными выражениями, другой программист теряет выходные плачу в свой четвертый Red Bull, пытаясь разгадать беспорядок, который я устроил. Имея это в виду, давайте углубимся . Создайте новый закрытый метод в классе Template с именем _parse_template () для обработки регулярных выражений, которые мы собираемся написать.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
<?php
  
/** * A templating engine * … */ class Template {
  
 /** * Stores the location of the template file * @var string */ public $template,
  
 /** * Stores the entries to be parsed in the template * @var array */ $entries = array();
  
 /** * Stores the contents of the template file * @var string */ private $_template;
  
 //TODO: Write a public method to output the result of the templating engine
  
 /** * Loads a template file with which markup should be formatted * … */ private function _load_template( ) {…}
  
 /** * Separates the template into header, loop, and footer for parsing * * @param array $extra Additional content for the header/footer * @return string The entry markup */ private function
_parse_template( $extra=NULL ) { //TODO: Remove any PHP-style comments from the template
  
 //TODO: Extract the main entry loop from the file
  
 //TODO: Extract the header from the template if one exists
  
 //TODO: Extract the footer from the template if one exists
  
 //TODO: Define a regex to match any template tag
  
 //TODO: Process each entry and insert its values into the loop
  
 //TODO: Curry the function that will replace the tags with entry data
  
 //TODO: If extra data was passed to fill in the header/footer, parse it here
  
 //TODO: Return the formatted entries with the header and footer }
  
 //TODO: Write a static method to replace the template tags with entry data
  
 //TODO: Write a private currying function to facilitate tag replacement
  
}

Для тестирования нам понадобятся три вещи:

  1. Файл шаблона для тестирования
  2. Метод для возврата сгенерированной разметки из класса Template
  3. Изменения в index.php, которые будут выводить возвращенную разметку

Сначала давайте соберем образец шаблона, который протестирует все функции, которые мы собираемся встроить в _parse_template ( . В assets / templates / создайте новый файл с именем template-test.inc и вставьте следующее:

1
2
3
4
5
6
7
8
9
/* * This is a test comment */ <h2>Template Header</h2>
  
{loop}
  
// Entry data will be processed here This is content that should be displayed.
  
{/loop}
  
/* * This is another block comment */ Template footer.

Далее нам нужно определить наш публичный метод для генерации разметки, который будет называться generate_markup () . Этот метод просто вызывает _load_template () и _parse_template () и выводит итоговую разметку HTML. Метод generate_markup () в конечном итоге примет дополнительные данные, которые можно вставить в верхний или нижний колонтитул шаблона, поэтому мы также подготовимся к этой функции, добавив аргумент в метод с именем $ extra .

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
<?php
  
/** * A templating engine * … */ class Template {
  
 /** * Stores the location of the template file * @var string */ public $template,
  
 /** * Stores the entries to be parsed in the template * @var array */ $entries = array();
  
 /** * Stores the contents of the template file * @var string */ private $_template;
  
 /** * Generates markup by inserting entry data into the template file * * @param array $extra Extra data for the header/footer * @return string The HTML with entry data inserted into the
template */ public function generate_markup( $extra=array() ) { $this->_load_template();
  
 /** * Loads a template file with which markup should be formatted * … */ private function _load_template( ) {…}
  
 /** * Separates the template into header, loop, and footer for parsing * … */ private function _parse_template( $extra=NULL ) {…}
  
 //TODO: Write a static method to replace the template tags with entry data
  
 //TODO: Write a private currying function to facilitate tag replacement
  
}

Наконец, давайте изменим index.php для вывода возвращаемого значения generate_markup () . Для этого используйте require_once, чтобы включить файл класса Template, а затем создайте его экземпляр. С нашим новым объектом Template мы можем определить имя файла шаблона и отобразить результат generate_markup () в браузере:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
<?php
  
// Error reporting is turned up to 11 for the purposes of this demo ini_set(«display_errors»,1);
  
// Exception handling set_exception_handler(&#39;exception_handler&#39;);
  
// Load the Template class require_once &#39;system/class.template.inc.php&#39;;
  
// Create a new instance of the Template class $template = new Template;
  
// Set the testing template file location $template->template_file = &#39;template-test.inc&#39;;
  
// Output the template markup echo $template->generate_markup();
  
/** * Loads entries from the Envato API for a given site * … */ function load_envato_blog_posts( $site=&#39;themeforest&#39; ) {…}

Теперь все, что нам нужно сделать, это временно
выведите содержимое шаблона внизу _parse_template (), чтобы мы могли видеть, что происходит, когда разбираем шаблон:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
<?php
  
/** * A templating engine * … */ class Template {
  
 /** * Stores the location of the template file * @var string */ public $template,
  
 /** * Stores the entries to be parsed in the template * @var array */ $entries = array();
  
 /** * Stores the contents of the template file * @var string */ private $_template;
  
 /** * Generates markup by inserting entry data into the template file * … */ public function generate_markup( $extra=array() ) {…}
  
 /** * Loads a template file with which markup should be formatted * … */ private function _load_template( ) {…}
  
 /** * Separates the template into header, loop, and footer for parsing * … */ private function _parse_template( $extra=NULL ) { //TODO: Remove any PHP-style comments from the template
  
 //TODO: Extract the main entry loop from the file
  
 //TODO: Extract the header from the template if one exists
  
 //TODO: Extract the footer from the template if one exists
  
 //TODO: Define a regex to match any template tag
  
 //TODO: Process each entry and insert its values into the loop
  
 //TODO: Curry the function that will replace the tags with entry data
  
 //TODO: If extra data was passed to fill in the header/footer, parse it here
  
 //TODO: Return the formatted entries with the header and footer
  
 // TEMPORARY: return the template after comment removal return $this->_template;
  
 //TODO: Write a static method to replace the template tags with entry data
  
 //TODO: Write a private currying function to facilitate tag replacement
  
}

Убедитесь, что это работает, загрузив index.php в вашем браузере. Это должно произвести следующее:

1
2
3
4
5
6
/* *
This is a test comment */ Template Header {loop} // Entry data will be processed here
  
This is content that should be displayed.
  
Template footer.

Наши первые регулярные выражения удаляют любые комментарии в стиле PHP из файла шаблона. Хотя это не является строго необходимым шагом, имейте в виду, что мы стараемся сделать эту систему максимально удобной для пользователей. Потенциальному разработчику шаблонов было бы чрезвычайно полезно узнать, какие теги доступны, но, вероятно, нежелательно, чтобы эти комментарии были в окончательной разметке (не говоря уже о том, что комментарий в стиле PHP нарушит макеты HTML). Два стиля комментариев, к которым мы собираемся обратиться, — это рекомендуемые стили комментариев в PHP:

1
2
3
/* * This is a block-level comment */
  
// This is a one-line comment

Во-первых, давайте сосредоточимся на регулярном выражении, которое будет фиксировать комментарии на уровне блоков. Это должно соответствовать любой строке, начинающейся с / * и заканчивающейся * / . Прямо из ворот наше регулярное выражение будет выглядеть так:

Поскольку стандартным разделителем регулярных выражений является косая черта ( / ), мы будем использовать альтернативный разделитель, чтобы уменьшить количество экранированных символов, необходимых в нашем регулярном выражении. Числовой знак ( # ) является вполне допустимым разделителем регулярных выражений — хотя я обычно рекомендую использовать стандартную косую черту для ясности, в некоторых случаях использование стандарта может снизить читаемость регулярного выражения. Сравните следующее:

Если мы вставим это регулярное выражение в _parse_template () как есть, это не даст желаемого результата. Добавьте смелый код ниже к _parse_template (), чтобы увидеть результат нашего текущего регулярного выражения:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<?php
  
class Template {
  
 public $template, $entries = array();
  
 private $_template;
  
 public function generate_markup( $extra=array() ) {…}
  
 private function _load_template( ) {…}
  
 private function _parse_template( $extra=NULL ) { // Create an alias of the template file property to save space $template = $this->_template;
  
 // Remove any PHP-style comments from the template $comment_pattern = ‘#/\*\*/#’;
  
 //TODO: To-do items snipped for brevity…
  
 // TEMPORARY: return the template after comment removal return $template;
  
 //TODO: Write a static method to replace the template tags with entry data
  
 //TODO: Write a private currying function to facilitate tag replacement
  
}

ПРИМЕЧАНИЕ. В оставшейся части этой статьи комментарии по docblock будут опущены для экономии места (если они не новые). Вывод в вашем браузере не изменится, если вы перезагрузите index.php ; это происходит потому, что мы не добавили подстановочный знак с модификатором, чтобы учесть ноль или более символов между открытием и закрытием комментария ( . * ). Кроме того, нам нужно учесть тот факт, что комментарии на уровне блоков обычно многострочные, нам нужно добавить модификатор s , чтобы учесть это. Наше модифицированное регулярное выражение должно выглядеть так:

Настройте это в _parse_template () , затем перезагрузите index.php в вашем браузере. Упс! Выход:

1
Template footer.

Мы забыли сделать подстановочный знак ленивым, поэтому вместо остановки в конце первого комментария он продолжился до конца второго комментария блока. Однако это легко исправить: просто добавьте знак вопроса после подстановочного знака, чтобы сделать его ленивым. Это должно выглядеть так:

Настройте _parse_template () , затем перезагрузите index.php . Намного лучше!

1
2
3
4
5
Template Header {loop} // Entry data will be processed here
  
This is content that should be displayed.
  
Template footer.

Затем нам нужно нацелить встроенные комментарии (те, которые начинаются с двух косых черт ( // ). Поскольку они не многострочные, на самом деле это будет собственное регулярное выражение вместо расширения регулярного выражения комментариев на уровне блоков. Для этого регулярного выражения нам нужно найти любой текст после двух прямых косых черт ( // ). Единственное исключение из этого правила — две прямые косые черты в удаленном URL ( http: // ) — чтобы исключить это, мы будем использовать отрицательный вид сзади . Готовое регулярное выражение должно выглядеть так:

Добавьте это к _parse_template () , изменив переменную $ comment_pattern на массив с нашим регулярным выражением уровня блока в качестве первого элемента и регулярным регулярным выражением комментария в качестве второго:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<?php
  
class Template {
  
 public $template, $entries = array();
  
 private $_template;
  
 public function generate_markup( $extra=array() ) {…}
  
 private function _load_template( ) {…}
  
 private function _parse_template( $extra=NULL ) { // Create an alias of the template file property to save space $template = $this->_template;
  
 // Remove any PHP-style comments from the template $comment_pattern = array(‘#/\*.*?\*/#s’, ‘#(?<!:)//.*#’);
  
 //TODO: To-do items snipped for brevity…
  
 // TEMPORARY: return the template after comment removal return $template;
  
 //TODO: Write a static method to replace the template tags with entry data
  
 //TODO: Write a private currying function to facilitate tag replacement
  
}

Теперь комментарии правильно удаляются при перезагрузке index.php в вашем браузере:

1
2
3
4
5
6
Template Header
{loop}
  
This is content that should be displayed.
  
Template footer.

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

Сначала мы возьмем цикл, перехватывая весь контент между тегами шаблона {loop} и {/ loop} . Это регулярное выражение будет соответствовать всему шаблону и использовать группу захвата для определения цикла:

Измените _parse_template (), как показано жирным шрифтом, чтобы проверить правильность извлечения цикла ввода:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<?php
  
class Template {
  
 public $template, $entries = array();
  
 private $_template;
  
 public function generate_markup( $extra=array() ) {…}
  
 private function _load_template( ) {…}
  
 private function _parse_template( $extra=NULL ) { // Create an alias of the template file property to save space $template = $this->_template;
  
 // Remove any PHP-style comments from the template $comment_pattern = array(‘#/\*.*?\*/#s’, ‘#(?<!:)//.*#’);
  
 // Extract the main entry loop from the file $pattern = ‘#.*{loop}(.*?){/loop}.*#is’;
  
 //TODO: To-do items snipped for brevity…
  
 // TEMPORARY: return the loop after isolating it return $entry_template;
  
 //TODO: Write a static method to replace the template tags with entry data
  
 //TODO: Write a private currying function to facilitate tag replacement
  
}

ПРИМЕЧАНИЕ. Не забудьте изменить функцию, чтобы она возвращала $ entry_template, чтобы вы могли видеть правильный вывод. Используя preg_replace () для «замены» всего шаблона только на захваченный шаблон цикла, мы успешно изолировали основной цикл. Перезагрузите index.php в вашем браузере, и вы должны увидеть следующее:

1
This is content that should be displayed.

Далее, давайте получим заголовок из шаблона. Регулярное выражение для этого будет похоже на то, которое перехватило основной цикл, но на этот раз мы собираемся захватить содержимое до тега шаблона {loop} . Чтобы соответствовать заголовку, нам нужно начать с начала шаблона и записывать все до тега {loop} . Поскольку мы используем preg_replace () для извлечения заголовка, нам также необходимо сопоставить все после тега {loop}, чтобы убедиться, что он удаляется при замене. Это регулярное выражение, когда оно завершено, должно выглядеть так:

Поскольку некоторые шаблоны не требуют заголовка, мы также должны убедиться, что данные, возвращаемые в заголовке, не являются целым шаблоном. Если это произойдет, заголовок должен быть установлен в NULL, чтобы избежать дублирования данных. Измените _parse_template () с помощью кода, выделенного жирным шрифтом, и установите его так, чтобы он возвращал извлеченный заголовок:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<?php
  
class Template {
  
 public $template, $entries = array();
  
 private $_template;
  
 public function generate_markup( $extra=array() ) {…}
  
 private function _load_template( ) {…}
  
 private function _parse_template( $extra=NULL ) { // Create an alias of the template file property to save space $template = $this->_template;
  
 // Remove any PHP-style comments from the template $comment_pattern = array(‘#/\*.*?\*/#s’, ‘#(?<!:)//.*#’);
  
 // Extract the main entry loop from the file $pattern = ‘#.*{loop}(.*?){/loop}.*#is’;
  
 // Extract the header from the template if one exists $header = trim(preg_replace(‘/^(.*)?{loop.*$/is’, «$1», $template));
  
 //TODO: To-do items snipped for brevity…
  
 // TEMPORARY: return the header after isolating it return $header;
  
 //TODO: Write a static method to replace the template tags with entry data
  
 //TODO: Write a private currying function to facilitate tag replacement
  
}

Как и ожидалось, перезагрузка index.php в вашем браузере приведет к отображению данных заголовка:

1
Template Header

Нижний колонтитул очень похож на верхний колонтитул в том, как он извлекается, за исключением того, что на этот раз мы собираем данные после тега {/ loop} и проверяем, чтобы они не соответствовали всему шаблону с помощью этого регулярного выражения:

Вставьте это в _parse_template () и установите метод для возврата содержимого нижнего колонтитула, как мы сделали с заголовком, используя жирный код:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<?php
  
class Template {
  
 public $template, $entries = array();
  
 private $_template;
  
 public function generate_markup( $extra=array() ) {...}
  
 private function _load_template( ) {...}
  
 private function _parse_template( $extra=NULL ) { // Create an alias of the template file property to save space $template = $this->_template;
  
 // Remove any PHP-style comments from the template $comment_pattern = array('#/\*.*?\*/#s', '#(?<!:)//.*#'); $template = preg_replace($comment_pattern, NULL, $template);
  
 // Extract the main entry loop from the file $pattern = '#.*{loop}(.*?){/loop}.*#is'; $entry_template = preg_replace($pattern, "$1", $template);
  
 }
  
 }
  
 //TODO: To-do items snipped for brevity...
  
 }
  
 //TODO: Write a static method to replace the template tags with entry data
  
 //TODO: Write a private currying function to facilitate tag replacement
  
}

Перезагрузите файл индекса, чтобы увидеть вывод нижнего колонтитула:

1
Template footer.

Следующим шагом в нашем процессе является создание регулярного выражения, которое будет соответствовать любому тегу шаблона, чтобы мы могли заменить их входными данными. В отличие от других, которые мы написали, этот не должен соответствовать всему шаблону. Этот шаблон должен соответствовать только тегу шаблона, который будет заменен . Для этого мы можем использовать сокращение, чтобы сопоставить любой символ слова ( \ w эквивалентен [A-Za-z0-9_] ) и сопоставить один или несколько символов между фигурными скобками. Полное регулярное выражение выглядит так:

Мы проверим, что это работает всего лишь немного, но сейчас давайте просто определим шаблон на потом, добавив жирный код в _parse_template () :

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<?php
  
class Template {
  
 public $template, $entries = array();
  
 private $_template;
  
 public function generate_markup( $extra=array() ) {...}
  
 private function _load_template( ) {...}
  
 private function _parse_template( $extra=NULL ) { // Create an alias of the template file property to save space $template = $this->_template;
  
 // Remove any PHP-style comments from the template $comment_pattern = array('#/\*.*?\*/#s', '#(?<!:)//.*#'); $template = preg_replace($comment_pattern, NULL, $template);
  
 // Extract the main entry loop from the file $pattern = '#.*{loop}(.*?){/loop}.*#is'; $entry_template = preg_replace($pattern, "$1", $template);
  
 }
  
 }
  
 // Define a regex to match any template tag $tag_pattern = '/{(\w+)}/';
  
 //TODO: To-do items snipped for brevity... }
  
 //TODO: Write a static method to replace the template tags with entry data
  
 //TODO: Write a private currying function to facilitate tag replacement
  
}

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


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

Карри — это довольно странная концепция, которая заставляет вас задуматься, поэтому давайте на секунду рассмотрим, как именно она работает. Цель функции каррирования — сделать возможным предоставление одного аргумента за раз функции, которая требует нескольких аргументов, не вызывая ошибок. Так, например, давайте посмотрим на простую математическую функцию:

1
}

Если бы мы хотели вызвать эту функцию как обычно, мы бы просто сделали следующее:

1
echo add(1, 2); // Output: 3

Но давайте скажем, что — по какой-то причине — нам нужно было вызывать функцию add () постепенно; мы не можем просто добавить один аргумент сейчас, а другой позже (вызывая add (1) ). Это выдает предупреждение:

1
2
3
Warning: Missing argument 2 for add()...
  
Notice: Undefined variable: y in ...

Поэтому нам нужен промежуточный шаг, который проверит, было ли передано правильное количество аргументов. Если это так, функция выполняется как обычно. Однако, если аргументов слишком мало, будет возвращена новая функция с именем функции и сохраненным первым аргументом. Таким образом, когда передан второй аргумент, исходная функция может быть вызвана правильно. Используя нашу функцию add () в качестве примера и предполагая, что мы можем карри эту функцию, используя воображаемую функцию curry () , мы можем продемонстрировать этот процесс:

1
2
3
4
5
6
7
/* * Start by currying the function - argument 1 is the function name, * argument 2 is the number of arguments expected */ $curried = curry('add', 2); // Returns the curried add function
  
$partial = $curried(1); // Returns a function with the argument '1' stored
  
echo $partial(2); // Output: 3
  
// Try passing the expected number of arguments to the curried function echo $curried(2, 2); // Output: 4

Теперь, когда мы знаем, как работает карри, давайте начнем писать функцию. Сама функция каррирования всегда будет возвращать функцию, которую мы будем делать с помощью create_function () . Созданная функция проверит, существует ли надлежащее количество аргументов, и выполнит функцию curried, как обычно, если это так, используя call_user_func_array () . Если аргументов недостаточно, будет возвращена другая функция с помощью create_function () . Поскольку объединение всего этого требует создания функций внутри созданных функций, существует много способов избежать этого. Это делает наш метод карри выглядит более запутанным, чем на самом деле. Добавьте его в класс Template с помощью следующего жирного кода:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
<?php
  
class Template {
  
 public $template, $entries = array();
  
 private $_template;
  
 public function generate_markup( $extra=array() ) {...}
  
 private function _load_template( ) {...}
  
 private function _parse_template( $extra=NULL ) {...}
  
 //TODO: Write a static method to replace the template tags with entry data
  
 /** * A currying function * * Currying allows a function to be called incrementally. This means that if * a function accepts two arguments, it can be curried with only one * argument supplied,
which returns a new function that will accept the * remaining argument and return the output of the original curried function * using the two supplied parameters. * * Example:
  
 }
  
 $func = $this->_curry(&#39;add&#39;, 2);
  
 $func2 = $func(1); // Stores 1 as the first argument of add()
  
 echo $func2(2); // Executes add() with 2 as the second arg and outputs 3
  
 * @param string $function The name of the function to curry * @param int $num_args The number of arguments the function accepts * @return mixed Function or return of the curried function */
private function _curry( $function, $num_args ) { return create_function(&#39;&#39;, " // Store the passed arguments in an array \$args = func_get_args();
  
 }
  
 // Export the function arguments as executable PHP code \$args = var_export(\$args, 1);
  
 // Return a new function with the arguments stored otherwise return create_function(&#39;&#39;,&#39; \$a = func_get_args(); \$z = &#39; . \$args . &#39;; \$a = array_merge(\$z,\$a); return
call_user_func_array(\&#39;$function\&#39;, \$a); &#39;); "); }
  
}

Сейчас может быть неясно, как все это относится к системе шаблонов. Когда мы перейдем к следующему шагу, мы рассмотрим это подробно, но вкратце нам нужно иметь возможность вызывать функцию с двумя аргументами, чтобы заменить теги шаблона: один аргумент — это запись, из которой должны быть получены данные. вытащил, а другой тег шаблона должен быть заменен. Поскольку мы используем регулярные выражения для замены тегов, нам нужно использовать preg_replace_callback () для выполнения замен. Однако, поскольку обратный вызов, переданный этой функции, может принимать только один аргумент — сопоставленный текст — нам нужно передать каррированную функцию, в которой уже есть запись, хранящаяся внутри нее. Есть смысл? Давайте сделаем это!


Все части на месте. Теперь нам просто нужно соединить точки и сделать это.

Замена тегов сама по себе довольно проста: возьмите соответствующий текст из тега шаблона и посмотрите, существует ли свойство в объекте ввода с таким именем. Если это так, вернуть данные, хранящиеся в указанном свойстве; если нет, просто верните тег шаблона, чтобы дизайнер мог видеть, что что-то пошло не так (или, в крайних случаях, нечетная строка, заключенная в фигурные скобки, не разрывается). Эта функция будет называться replace_tags () , и она будет статической, чтобы ее можно было передать функции каррирования в качестве допустимого обратного вызова . Добавьте его в класс Template, используя жирный код ниже:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?php
  
class Template {
  
 public $template, $entries = array();
  
 private $_template;
  
 public function generate_markup( $extra=array() ) {...}
  
 private function _load_template( ) {...}
  
 private function _parse_template( $extra=NULL ) {...}
  
 /** * Replaces template tags with the corresponding entry data * * @param string $entry A serialized entry object * @param array $params Parameters for replacement * @param array $matches The match array from preg_replace_callback() * @return string The replaced template value */ public static function replace_tags($entry, $matches) { // Unserialize the object $entry = unserialize($entry);
  
 }
  
 } }
  
 private function _curry( $function, $num_args ) {...}
  
}

ПРИМЕЧАНИЕ . Вызов unserialize () в начале этого метода происходит из-за проблемы с передачей объекта через функцию каррирования. Мы сериализуем объект на следующем шаге.

Чтобы завершить нашу систему шаблонов, нам сначала нужно подготовить нашу функцию обратного вызова карри для использования со всеми вызовами замены. Это делается путем каррирования Template :: replace_tags () и сохранения его в переменной с именем $ callback . Добавьте это к _parse_template () с помощью полужирного кода ниже:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<?php
  
class Template {
  
 public $template, $entries = array();
  
 private $_template;
  
 public function generate_markup( $extra=array() ) {...}
  
 private function _load_template( ) {...}
  
 private function _parse_template( $extra=NULL ) { // Create an alias of the template file property to save space $template = $this->_template;
  
 // Remove any PHP-style comments from the template $comment_pattern = array('#/\*.*?\*/#s', '#(?<!:)//.*#'); $template = preg_replace($comment_pattern, NULL, $template);
  
 // Extract the main entry loop from the file $pattern = '#.*{loop}(.*?){/loop}.*#is'; $entry_template = preg_replace($pattern, "$1", $template);
  
 }
  
 }
  
 // Define a regex to match any template tag $tag_pattern = '/{(\w+)}/';
  
 // Curry the function that will replace the tags with entry data $callback = $this->_curry('Template::replace_tags', 2);
  
 //TODO: To-do items snipped for brevity... }
  
 public static function replace_tags($entry, $matches) {...}
  
 private function _curry( $function, $num_args ) {...}
  
}

Далее нам нужно настроить цикл для прохождения каждого объекта записи в массиве $ records . Для каждой записи нам нужно вызвать preg_replace_callback () с тегом шаблона regex в качестве первого аргумента, обратным вызовом с сериализованным объектом записи в качестве второго аргумента и циклом в качестве третьего аргумента. Разметка, возвращаемая при каждом вызове, должна добавляться к переменной с именем $ markup , в которой хранится вся разметка записи, которая должна быть выведена в браузер. Добавьте это к _parse_template () с жирным кодом:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
<?php
  
class Template {
  
 public $template, $entries = array();
  
 private $_template;
  
 public function generate_markup( $extra=array() ) {...}
  
 private function _load_template( ) {...}
  
 private function _parse_template( $extra=NULL ) { // Create an alias of the template file property to save space $template = $this->_template;
  
 // Remove any PHP-style comments from the template $comment_pattern = array('#/\*.*?\*/#s', '#(?<!:)//.*#'); $template = preg_replace($comment_pattern, NULL, $template);
  
 // Extract the main entry loop from the file $pattern = '#.*{loop}(.*?){/loop}.*#is'; $entry_template = preg_replace($pattern, "$1", $template);
  
 }
  
 }
  
 // Define a regex to match any template tag $tag_pattern = '/{(\w+)}/';
  
 // Curry the function that will replace the tags with entry data $callback = $this->_curry('Template::replace_tags', 2);
  
 // Process each entry and insert its values into the loop $markup = NULL; for( $i=0, $c=count($this->entries); $i<$c; ++$i ) { $markup .= preg_replace_callback( $tag_pattern,
}
  
 }
  
 public static function replace_tags($entry, $matches) {...}
  
 private function _curry( $function, $num_args ) {...}
  
}

Прежде чем мы сможем это проверить, нам нужно создать запись, чтобы у движка было что-то, что можно было бы пройти. В index.php добавьте фиктивную запись со свойством
$ test, чтобы соответствовать тегу шаблона в нашем файле шаблона тестирования:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
<?php
  
// Error reporting is turned up to 11 for the purposes of this demo ini_set("display_errors",1); ERROR_REPORTING(E_ALL);
  
}
  
// Load the Template class require_once &#39;system/class.template.inc.php&#39;;
  
// Create a new instance of the Template class $template = new Template;
  
// Set the testing template file location $template->template_file = &#39;template-test.inc&#39;;
  
$template->entries[] = (object) array( 'test' => 'This was inserted using template tags!' );
  
// Output the template markup echo $template->generate_markup();
  
/** * Loads entries from the Envato API for a given site * ... */ function load_envato_blog_posts( $site=&#39;themeforest&#39; ) {...}

При наличии фиктивной записи мы можем проверить замену тега шаблона, перезагрузив index.php в нашем браузере. Вывод должен гласить:

1
This is content that should be displayed. This was inserted using template tags!

Это означает, что мы эффективно создали шаблонизатор! Однако мы еще не закончили: нам все еще нужно добавить возможность замены тегов шаблона в верхнем и нижнем колонтитулах нашего файла шаблона.

Процесс замены тегов шаблонов верхнего и нижнего колонтитула идентичен процессу для тегов в цикле, но для этого необходимы другие данные. Возможно, вы помните, что мы включили аргумент $ extra, когда писали методы generate_markup () и _parse_template () ; эта переменная должна использоваться как объект, который будет хранить данные для замены тегов шаблона верхнего и нижнего колонтитула. Для наших целей $ extra может содержать два свойства, $ header и $ footerоба из которых будут хранить объект, свойства которого будут использоваться для замены тегов шаблона в соответствующем разделе шаблона. Очевидно, что если нет тегов верхнего или нижнего колонтитула, то никакие данные не будут сохраняться в $ extra для обработки. По этой причине мы начнем с проверки, является ли $ extra объектом. Если это так, мы пройдемся по его свойствам и запустим preg_replace_callback () с тегом regex шаблона, обратным вызовом после передачи сериализованного объекта верхнего или нижнего колонтитула и раздела верхнего или нижнего колонтитула шаблона. Добавьте жирный код ниже, чтобы завершить нашу систему шаблонов:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
<?php
  
class Template {
  
 public $template, $entries = array();
  
 private $_template;
  
 public function generate_markup( $extra=array() ) {...}
  
 private function _load_template( ) {...}
  
 private function _parse_template( $extra=NULL ) { // Create an alias of the template file property to save space $template = $this->_template;
  
 // Remove any PHP-style comments from the template $comment_pattern = array('#/\*.*?\*/#s', '#(?<!:)//.*#'); $template = preg_replace($comment_pattern, NULL, $template);
  
 // Extract the main entry loop from the file $pattern = '#.*{loop}(.*?){/loop}.*#is'; $entry_template = preg_replace($pattern, "$1", $template);
  
 }
  
 }
  
 // Define a regex to match any template tag $tag_pattern = '/{(\w+)}/';
  
 // Curry the function that will replace the tags with entry data $callback = $this->_curry('Template::replace_tags', 2);
  
 // Process each entry and insert its values into the loop $markup = NULL; for( $i=0, $c=count($this->entries); $i<$c; ++$i ) { $markup .= preg_replace_callback( $tag_pattern,
}
  
 // If extra data was passed to fill in the header/footer, parse it here if( is_object($extra) ) { foreach( $extra as $key=>$props ) { $$key = preg_replace_callback( $tag_pattern,
} }
  
 }
  
 public static function replace_tags($entry, $matches) {...}
  
 private function _curry( $function, $num_args ) {...}
  
}

Если вы перезагрузите index.php в своем браузере, вы увидите следующий вывод:

1
2
3
4
5
Template Header
  
This is content that should be displayed. This was inserted using template tags!
  
Template footer.

Последнее, что нужно сделать, это проверить замену тега шаблона верхнего и нижнего колонтитула. Откройте template-test.inc и добавьте два новых тега шаблона, один в верхнем колонтитуле и один в нижнем колонтитуле:

1
2
3
4
5
6
7
8
9
/* * This is a test comment */ <h2>Template Header</h2> {header_stuff}
  
{loop}
  
// Entry data will be processed here This is content that should be displayed. {test}
  
{/loop}
  
/* * This is another block comment */ Template footer. {footerStuff}

Затем вернитесь в index.php и добавьте новый объект с именем $ extra, в котором два объекта
хранятся в свойствах $ header и $ footer, свойства которых соответствуют тегам нового шаблона:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
<?php
  
// Error reporting is turned up to 11 for the purposes of this demo ini_set("display_errors",1); ERROR_REPORTING(E_ALL);
  
}
  
// Load the Template class require_once &#39;system/class.template.inc.php&#39;;
  
// Create a new instance of the Template class $template = new Template;
  
// Set the testing template file location $template->template_file = &#39;template-test.inc&#39;;
  
$template->entries[] = (object) array( 'test' => 'This was inserted using template tags!' );
  
$extra = (object) array( 'header' => (object) array( 'header_stuff' => 'Some extra content.' ), 'footer' => (object) array( 'footerStuff' => 'More extra content.' ) );
  
// Output the template markup echo $template->generate_markup($extra);
  
/** * Loads entries from the Envato API for a given site * ... */ function load_envato_blog_posts( $site=&#39;themeforest&#39; ) {...}

ПРИМЕЧАНИЕ: не забудьте передать $ extra для generate_markup () ! Сохраните эти изменения, перезагрузите файл в браузере, и вы увидите следующее:

1
2
3
4
5
Template Header Some extra content.
  
This is content that should be displayed. This was inserted using template tags!
  
Template footer. More extra content.

В качестве последнего упражнения давайте воспользуемся некоторыми реальными записями из Envato Marketplace и разработаем шаблон для их отображения.

Для шаблона давайте создадим новое в папке шаблонов с именем entry-list.inc . Внутри добавьте следующий код:

1
2
3
4
5
6
7
8
9
/** * This template has the following tags available: * title Article title * url Permalink of the article * site Site on which the article was originally published * posted_at Original posting date */
  
<h3>Entry Short List</h3>
  
<ol id="entry-list">
  
// This is the main entry loop {loop} <li><a href="{url}">{title}</a> (published on {site})</li> {/loop}
  
</ol><!-- end #entry-list -->

Все, что нам нужно сделать для загрузки реальных записей из API Envato, — это вызвать функцию, которую мы написали ранее в этом руководстве. В
index.php , измените код, чтобы использовать новый файл шаблона и загружать последние записи из audiojungle :

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<?php
  
// Error reporting is turned up to 11 for the purposes of this demo ini_set("display_errors",1); ERROR_REPORTING(E_ALL);
  
}
  
?> <!DOCTYPE html> <html lang="en">
  
<head>
  
 <meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
  
 <!-- Meta information --> <title>Demo: Roll Your Own Templating System in PHP</title> <meta name="description" content="A demo of the templating system by Jason Lengstorf" /> </head>
  
<body> <?php
  
// Load the Template class require_once &#39;system/class.template.inc.php&#39;;
  
// Create a new instance of the Template class $template = new Template;
  
// Set the testing template file location $template->template_file = &#39;entry-list.inc&#39;;
  
// Load sample entries from Envato $template->entries = load_envato_blog_posts(&#39;audiojungle&#39;);
  
// Output the template markup echo $template->generate_markup();
  
?> </body> </html> <?php
  
/** * Loads entries from the Envato API for a given site * ... */ function load_envato_blog_posts( $site=&#39;themeforest&#39; ) {...}

ПРИМЕЧАНИЕ. Я добавил декларацию типа документа и основные теги HTML, чтобы избежать проблем с кодировкой символов.


К этому моменту вы успешно объединили объектно-ориентированный PHP, регулярные выражения и функции карри в простую в использовании систему шаблонов. Методы, описанные в этом руководстве, могут быть добавлены в ваш арсенал разработки для использования в будущих проектах, и, надеюсь, вы сейчас чувствуете себя лучшим разработчиком. Вы заметили ярлык, который я пропустил? Можете ли вы придумать способ улучшить систему шаблонов? Как вы относитесь к использованию этой системы в проекте, чтобы отделить разметку от бизнес-логики ? Дайте мне знать ваши мысли в комментариях!