1: <?php
2:
3: 4: 5: 6: 7: 8: 9: 10: 11:
12:
13:
14:
15: 16: 17: 18: 19: 20:
21: abstract class NBaseTemplate extends NObject implements ITemplate
22: {
23:
24: public $warnOnUndefined = TRUE;
25:
26:
27: public $onPrepareFilters = array();
28:
29:
30: private $params = array();
31:
32:
33: private $filters = array();
34:
35:
36: private $helpers = array();
37:
38:
39: private $helperLoaders = array();
40:
41:
42:
43: 44: 45: 46: 47:
48: public function registerFilter($callback)
49: {
50: $callback = callback($callback);
51: if (in_array($callback, $this->filters)) {
52: throw new InvalidStateException("Filter '$callback' was registered twice.");
53: }
54: $this->filters[] = $callback;
55: }
56:
57:
58:
59: 60: 61: 62:
63: final public function getFilters()
64: {
65: return $this->filters;
66: }
67:
68:
69:
70:
71:
72:
73:
74: 75: 76: 77: 78:
79: public function render()
80: {
81: }
82:
83:
84:
85: 86: 87: 88: 89:
90: public function __toString()
91: {
92: ob_start();
93: try {
94: $this->render();
95: return ob_get_clean();
96:
97: } catch (Exception $e) {
98: ob_end_clean();
99: if (func_num_args() && func_get_arg(0)) {
100: throw $e;
101: } else {
102: NDebug::toStringException($e);
103: }
104: }
105: }
106:
107:
108:
109: 110: 111: 112: 113: 114:
115: protected function compile($content, $label = NULL)
116: {
117: if (!$this->filters) {
118: $this->onPrepareFilters($this);
119: }
120:
121: try {
122: foreach ($this->filters as $filter) {
123: $content = self::extractPhp($content, $blocks);
124: $content = $filter->invoke($content);
125: $content = strtr($content, $blocks);
126: }
127: } catch (Exception $e) {
128: throw new InvalidStateException("Filter $filter: " . $e->getMessage() . ($label ? " (in $label)" : ''), 0, $e);
129: }
130:
131: if ($label) {
132: $content = "<?php\n// $label\n//\n?>$content";
133: }
134:
135: return self::optimizePhp($content);
136: }
137:
138:
139:
140:
141:
142:
143:
144: 145: 146: 147: 148: 149:
150: public function registerHelper($name, $callback)
151: {
152: $this->helpers[strtolower($name)] = callback($callback);
153: }
154:
155:
156:
157: 158: 159: 160: 161:
162: public function registerHelperLoader($callback)
163: {
164: $this->helperLoaders[] = callback($callback);
165: }
166:
167:
168:
169: 170: 171: 172:
173: final public function getHelpers()
174: {
175: return $this->helpers;
176: }
177:
178:
179:
180: 181: 182: 183: 184: 185:
186: public function __call($name, $args)
187: {
188: $lname = strtolower($name);
189: if (!isset($this->helpers[$lname])) {
190: foreach ($this->helperLoaders as $loader) {
191: $helper = $loader->invoke($lname);
192: if ($helper) {
193: $this->registerHelper($lname, $helper);
194: return $this->helpers[$lname]->invokeArgs($args);
195: }
196: }
197: return parent::__call($name, $args);
198: }
199:
200: return $this->helpers[$lname]->invokeArgs($args);
201: }
202:
203:
204:
205: 206: 207: 208: 209:
210: public function setTranslator(ITranslator $translator = NULL)
211: {
212: $this->registerHelper('translate', $translator === NULL ? NULL : array($translator, 'translate'));
213: return $this;
214: }
215:
216:
217:
218:
219:
220:
221:
222: 223: 224: 225: 226: 227:
228: public function add($name, $value)
229: {
230: if (array_key_exists($name, $this->params)) {
231: throw new InvalidStateException("The variable '$name' exists yet.");
232: }
233:
234: $this->params[$name] = $value;
235: }
236:
237:
238:
239: 240: 241: 242: 243:
244: public function setParams(array $params)
245: {
246: $this->params = $params;
247: return $this;
248: }
249:
250:
251:
252: 253: 254: 255:
256: public function getParams()
257: {
258: return $this->params;
259: }
260:
261:
262:
263: 264: 265: 266: 267: 268:
269: public function __set($name, $value)
270: {
271: $this->params[$name] = $value;
272: }
273:
274:
275:
276: 277: 278: 279: 280:
281: public function &__get($name)
282: {
283: if ($this->warnOnUndefined && !array_key_exists($name, $this->params)) {
284: trigger_error("The variable '$name' does not exist in template.", E_USER_NOTICE);
285: }
286:
287: return $this->params[$name];
288: }
289:
290:
291:
292: 293: 294: 295: 296:
297: public function __isset($name)
298: {
299: return isset($this->params[$name]);
300: }
301:
302:
303:
304: 305: 306: 307: 308:
309: public function __unset($name)
310: {
311: unset($this->params[$name]);
312: }
313:
314:
315:
316:
317:
318:
319:
320: 321: 322: 323: 324: 325:
326: private static function extractPhp($source, & $blocks)
327: {
328: $res = '';
329: $blocks = array();
330: $tokens = token_get_all($source);
331: foreach ($tokens as $n => $token) {
332: if (is_array($token)) {
333: if ($token[0] === T_INLINE_HTML) {
334: $res .= $token[1];
335: continue;
336:
337: } elseif ($token[0] === T_OPEN_TAG && $token[1] === '<?' && isset($tokens[$n+1][1]) && $tokens[$n+1][1] === 'xml') {
338: $php = & $res;
339: $token[1] = '<<?php ?>?';
340:
341: } elseif ($token[0] === T_OPEN_TAG || $token[0] === T_OPEN_TAG_WITH_ECHO) {
342: $res .= $id = "\x01@php:p" . count($blocks) . "@\x02";
343: $php = & $blocks[$id];
344: }
345: $php .= $token[1];
346:
347: } else {
348: $php .= $token;
349: }
350: }
351: return $res;
352: }
353:
354:
355:
356: 357: 358: 359: 360:
361: public static function optimizePhp($source)
362: {
363: $res = $php = '';
364: $lastChar = ';';
365: $tokens = new ArrayIterator(token_get_all($source));
366: foreach ($tokens as $key => $token) {
367: if (is_array($token)) {
368: if ($token[0] === T_INLINE_HTML) {
369: $lastChar = '';
370: $res .= $token[1];
371:
372: } elseif ($token[0] === T_CLOSE_TAG) {
373: $next = isset($tokens[$key + 1]) ? $tokens[$key + 1] : NULL;
374: if (substr($res, -1) !== '<' && preg_match('#^<\?php\s*$#', $php)) {
375: $php = '';
376:
377: } elseif (is_array($next) && $next[0] === T_OPEN_TAG) {
378: if ($lastChar !== ';' && $lastChar !== '{' && $lastChar !== '}' && $lastChar !== ':' && $lastChar !== '/' ) $php .= $lastChar = ';';
379: if (substr($next[1], -1) === "\n") $php .= "\n";
380: $tokens->next();
381:
382: } elseif ($next) {
383: $res .= preg_replace('#;?(\s)*$#', '$1', $php) . $token[1];
384: $php = '';
385:
386: } else {
387: if ($lastChar !== '}' && $lastChar !== ';') $php .= ';';
388: }
389:
390: } elseif ($token[0] === T_ELSE || $token[0] === T_ELSEIF) {
391: if ($tokens[$key + 1] === ':' && $lastChar === '}') $php .= ';';
392: $lastChar = '';
393: $php .= $token[1];
394:
395: } else {
396: if (!in_array($token[0], array(T_WHITESPACE, T_COMMENT, T_DOC_COMMENT, T_OPEN_TAG))) $lastChar = '';
397: $php .= $token[1];
398: }
399: } else {
400: $php .= $lastChar = $token;
401: }
402: }
403: return $res . $php;
404: }
405:
406: }
407: