Overview

Namespaces

  • Sleepy
  • Module
    • Authentication
    • CSV
    • DB
    • FormBuilder
    • FSDB
    • IP2Country
    • Mailer
    • MobiDetect
    • Navigation
    • StaticCache
  • PHP

Classes

  • Sleepy\Debug
  • Sleepy\Hook
  • Sleepy\Router
  • Sleepy\SM
  • Sleepy\Template

Exceptions

  • Sleepy\RouteNotFound
  • Overview
  • Namespace
  • Class
  • Tree
  • Deprecated
  • Todo
  1: <?php
  2: namespace Sleepy;
  3: 
  4: /**
  5:  * Provides templating functionality
  6:  *
  7:  * ## Usage
  8:  *
  9:  * ### PHP file: *index.php*
 10:  *
 11:  * <code>
 12:  *     require_once('include/sleepy.php');
 13:  *
 14:  *     $page = new Template('templates/default.tpl');
 15:  *     $page->bind('title', 'Sleepy Mustache');
 16:  *     $page->bind('header', 'Hello world!');
 17:  *     $page->show();
 18:  * </code>
 19:  *
 20:  * ### Template file: *\app\templates\default.tpl*
 21:  *
 22:  * <code>
 23:  *     <html>
 24:  *         <head>
 25:  *             <title>{{ title }}</title>
 26:  *         </head>
 27:  *         <body>
 28:  *             <h1>{{ header }}</h1>
 29:  *             <p>This page has been viewed {{ hits }} times.</p>
 30:  *         </body>
 31:  *     </html>
 32:  * </code>
 33:  *
 34:  * ## Changelog
 35:  *
 36:  * ### Version 1.7
 37:  * * Updated private prefix (_) for consistency
 38:  * * Updated documentation
 39:  *
 40:  * ### Version 1.6
 41:  * * No longer dependant on Hooks Module
 42:  *
 43:  * @todo add #if
 44:  *
 45:  * @date June 18, 2016
 46:  * @author Jaime A. Rodriguez <hi.i.am.jaime@gmail.com>
 47:  * @version 1.7
 48:  * @license  http://opensource.org/licenses/MIT
 49:  */
 50: 
 51: class Template {
 52:     /**
 53:      * The extension for template files
 54:      *
 55:      * @var string
 56:      */
 57:     public $extension = '.tpl';
 58: 
 59:     /**
 60:      * The template directory
 61:      *
 62:      * @var string
 63:      */
 64:     public $directory;
 65: 
 66:     /**
 67:      * The template file
 68:      *
 69:      * @var string
 70:      * @protected
 71:      */
 72:     protected $_file;
 73: 
 74:     /**
 75:      * The data bound to the template
 76:      *
 77:      * @var mixed[]
 78:      * @protected
 79:      */
 80:     protected $_data = array();
 81: 
 82:     /**
 83:      * The constructor
 84:      *
 85:      * @param string $template The name of the template
 86:      * @param string $basedir  The base directory for template files
 87:      */
 88:     public function __construct($template='', $basedir='') {
 89:         if (class_exists('\Sleepy\Hook')) {
 90:             \Sleepy\Hook::addAction('template_start');
 91:         }
 92: 
 93:         // If they didn't pass a basedir then try the default
 94:         if ($basedir == '') {
 95:             if (!defined('DIRBASE')) {
 96:                 define('DIRBASE', $_SERVER['DOCUMENT_ROOT'] . '/app');
 97:             }
 98: 
 99:             $this->directory = DIRBASE . '/templates/';
100:         } else {
101:             $this->directory = $basedir;
102:         }
103: 
104:         if (!empty($template)) {
105:             $this->setTemplate($template);
106:         }
107:     }
108: 
109:     /**
110:      * Does the template exist?
111:      *
112:      * @param  string $file Name of template
113:      * @return Bool         True if template exists
114:      * @private
115:      */
116:     private function _checkTemplate($file) {
117:         if (empty($file)) {
118:             throw new \Exception('Template file has not been set.');
119:         }
120: 
121:         // Check that the directory is set correctly
122:         if (!file_exists($this->directory)) {
123:             throw new \Exception("Template directory '{$this->directory}' does not exists.");
124:         }
125: 
126:         // Check if the template exists in the directory
127:         if (!file_exists($this->directory . $file . $this->extension)) {
128:             throw new \Exception("Template '{$this->directory}{$file}{$this->extension}' does not exist.");
129:         }
130: 
131:         return true;
132:     }
133: 
134:     /**
135:      * Given a path, the function returns a piece of $arr. For example
136:      * 'name.first' will return $arr['name']['first']
137:      *
138:      * @param  array  $arr  An array to search using the $path
139:      * @param  string $path A path representing the dimensions of the array
140:      * @return mixed        A sub-array or string
141:      * @private
142:      */
143:     private function _assignArrayByPath($arr, $path) {
144: 
145:         $keys = explode('.', $path);
146: 
147:         if (is_array($keys)) {
148:             foreach ($keys as $key) {
149:                 if (array_key_exists($key, $arr)) {
150:                     $arr = $arr[$key];
151:                 } else {
152:                     return false;
153:                 }
154:             }
155:         }
156: 
157:         return $arr;
158:     }
159: 
160:     /**
161:      * Renders the template
162:      *
163:      * @param  string $template The template to render
164:      * @param  array $data      The data bound to the template
165:      * @return string           The rendered template
166:      * @private
167:      */
168:     private function _render($template, $data) {
169:         // Process the includes
170:         if (preg_match('/{{\s*#include\s.*}}/', $template, $include)) {
171:             $index = trim(str_replace('{{', '', str_replace('}}', '', $include[0])));
172:             if (file_exists($this->directory . str_replace('#include ', '', $index) . $this->extension)) {
173:                 ob_start();
174:                 include($this->directory . str_replace('#include ', '', $index) . $this->extension);
175:             } else {
176:                 ob_clean(); // clear buffer in $this->show();
177:                 throw new \Exception($this->directory . str_replace('#include ', '', $index) . $this->extension . ' doesn\'t exist. Cannot include file.');
178:             }
179:             $template = str_replace($include[0], $this->_render(ob_get_clean(), $data), $template);
180: 
181:             return $this->_render($template, $data);
182:         }
183: 
184:         // Process the #each blocks
185:         if (preg_match_all('/{{\s?#each.+?}}(?:(?>[^{}]+)|(?R))*{{\s?\/each\s?}}/ism', $template, $loops)) {
186:             // For every #each
187:             foreach ($loops[0] as $value) {
188:                 // Reset rendered data
189:                 $rendered = '';
190: 
191:                 // Stores the values of <for> and <in> into $forin
192:                 preg_match('/{{\s?#each\s(?<for>\w+) in (?<in>.*?)\s?}}/', $value, $forin);
193: 
194:                 $forin['in'] = strtolower($forin['in']);
195: 
196:                 // Removes the each loop
197:                 $new_template = preg_replace('/{{\s?#each.*?}}/s', '', $value, 1);
198:                 $new_template = preg_replace('/{{\s?\/each\s?}}$/s', '', $new_template, 1);
199: 
200:                 // get the array based on the <in>
201:                 $in = $this->_assignArrayByPath($data, $forin['in']);
202: 
203:                 // for each changelog
204:                 if (is_array($in[0])) {
205: 
206:                     // Allow hooks to edit the data
207:                     if (class_exists('\Sleepy\Hook')) {
208:                         $in = \Sleepy\Hook::addFilter('template_each_array', array($in));
209:                     }
210: 
211:                     $iterator = 0;
212: 
213:                     foreach ($in as $new_data) {
214:                         $iterator++;
215: 
216:                         if (class_exists('\Sleepy\Hook')) {
217:                             $new_data = \Sleepy\Hook::addFilter('template_each', array($new_data));
218:                             $new_data = \Sleepy\Hook::addFilter('template_each_' + $forin['for'], array($new_data));
219:                         }
220: 
221:                         $new_data['iterator'] = $iterator;
222:                         $new_data['zebra'] = ($iterator % 2) ? 'odd' : 'even';
223: 
224:                         // Make the $new_data match the <for>
225:                         $new_data[$forin['for']] =  $new_data;
226: 
227:                         // render the new template
228:                         $rendered = $rendered . $this->_render($new_template, $new_data);
229:                     }
230:                 } else {
231:                     // render the new template
232:                     $rendered = $rendered . $this->_render($new_template, $data);
233:                 }
234: 
235:                 $template = str_replace($value, $rendered, $template);
236:             }
237:         }
238: 
239:         if (class_exists('\Sleepy\Hook')) {
240:             $template = \Sleepy\Hook::addFilter('prerender_template', $template);
241:         }
242: 
243:         // Find all the single placeholders
244:         preg_match_all('/{{\s?(.*?)(\s.*?)?\s?}}/', $template, $matches);
245: 
246:         // For each replace with a value
247:         foreach (array_unique($matches[0]) as $index => $placeholder) {
248:             $key = strtolower($matches[1][$index]);
249: 
250:             $arguments = array(
251:                 $this->_assignArrayByPath($data, $key)
252:             );
253: 
254:             # We trim so that there are no extra blank arguments
255:             $arguments = array_merge($arguments, explode(' ', trim($matches[2][$index])));
256: 
257:             $boundData = $arguments;
258: 
259:             if (class_exists('\Sleepy\Hook')) {
260:                 $boundData = \Sleepy\Hook::addFilter('render_placeholder_' . strtolower($key), $boundData);
261:             }
262: 
263:             // Some filters might take arrays and return only a single value, if
264:             // hooks are disabled, lets return only this single value
265:             if (is_array($boundData)) {
266:                 $boundData = $boundData[0];
267:             }
268: 
269:             $template = str_replace($placeholder, $boundData, $template);
270:         }
271: 
272:         return $template;
273:     }
274: 
275:     /**
276:      * Parses the template after it's been setup
277:      *
278:      * @return string The rendered template
279:      * @private
280:      */
281:     private function _parseTemplate() {
282:         $this->_checkTemplate($this->_file);
283: 
284:         // Render template file
285:         ob_start();
286:         include($this->directory . $this->_file . $this->extension);
287:         $template = $this->_render(ob_get_clean(), $this->_data);
288: 
289:         if (class_exists('\Sleepy\Hook')) {
290:             $template = \Sleepy\Hook::addFilter('render_template_' . $this->_file, $template);
291:             $template = \Sleepy\Hook::addFilter('render_template', $template);
292:         }
293: 
294:         return $template;
295:     }
296: 
297:     /**
298:      * Sets the template to use.
299:      *
300:      * @param [type] $file [description]
301:      */
302:     public function setTemplate($file) {
303:         if ($this->_checkTemplate($file)) {
304:             $this->_file = $file;
305:         }
306:     }
307: 
308:     /**
309:      * Binds data to the template placeholders
310:      *
311:      * @param  string $placeholder   The template placeholder
312:      * @param  mixed  $value         The value that replaced the placeholder
313:      */
314:     public function bind($placeholder, $value) {
315:         $placeholder = strtolower($placeholder);
316: 
317:         if (!is_array($value)) {
318:             if (class_exists('\Sleepy\Hook')) {
319:                 $value = \Sleepy\Hook::addFilter('bind_placeholder_' . $placeholder, $value);
320:             }
321:         }
322: 
323:         $this->_data[trim($placeholder)] = $value;
324:     }
325: 
326:     /**
327:      * Starts a buffer that will bind data to the template placeholders. The
328:      * buffer will capture anything you output until $this->bindStop()
329:      */
330:     public function bindStart() {
331:         ob_start();
332:     }
333: 
334:     /**
335:      * Stops the buffer that binds data to the template placeholders
336:      *
337:      * @param  string $placeholder   The template placeholder
338:      */
339:     public function bindStop($placeholder) {
340:         $content = ob_get_clean();
341: 
342:         if (class_exists('\Sleepy\Hook')) {
343:             $content = \Sleepy\Hook::addFilter('bind_placeholder_' . $placeholder, $content);
344:         }
345: 
346:         $this->_data[trim(strtolower($placeholder))] = $content;
347:     }
348: 
349:     /**
350:      * Gets the data for a placeholder
351:      *
352:      * @param  string $placeholder The placeholder
353:      * @return mixed               The data stored in the placeholder
354:      */
355:     public function get($key) {
356:         $value = $this->_data[$key];
357: 
358:         if (class_exists('\Sleepy\Hook')) {
359:             \Sleepy\Hook::addFilter('template_get_' . $key, $value);
360:         }
361: 
362:         return $value;
363:     }
364: 
365:     /**
366:      * Shows the rendered template
367:      */
368:     public function show() {
369:         echo $this->_parseTemplate();
370:     }
371: 
372:     /**
373:      * Retrieves the rendered template
374:      */
375:     public function retrieve() {
376:         return $this->_parseTemplate();
377:     }
378: }
sleepyMUSTACHE v.0.8 API documentation generated by ApiGen