1: <?php
2:
3: namespace Deimos\Flow;
4:
5: class Flow
6: {
7:
8: 9: 10:
11: protected $configure;
12:
13: 14: 15:
16: protected $viewPath;
17:
18: 19: 20:
21: protected $cachePath;
22:
23: 24: 25:
26: protected $view;
27:
28: 29: 30:
31: protected $curView;
32:
33: 34: 35:
36: public $foreach;
37:
38: 39: 40: 41: 42:
43: public function __construct(Configure $configure = null)
44: {
45: if (!$configure)
46: {
47: $configure = new Configure();
48: }
49:
50: $this->configure = $configure;
51: $this->foreach = new FlowForeach();
52: }
53:
54: 55: 56: 57: 58: 59: 60:
61: public function setTemplateDir($path)
62: {
63: $this->configure->template($path);
64:
65: return $this;
66: }
67:
68: 69: 70: 71: 72: 73: 74:
75: public function setCompileDir($path)
76: {
77: $this->configure->compile($path);
78:
79: return $this;
80: }
81:
82: 83: 84: 85: 86: 87: 88:
89: protected function view($view = null)
90: {
91: if (!$this->view)
92: {
93: $this->view = preg_replace('~(\.tpl)$~', '', $view);
94: $this->viewPath = $this->configure->template() . $this->view . $this->configure->ext();
95: }
96:
97: return $this->view;
98: }
99:
100: 101: 102:
103: public function selectView()
104: {
105: return $this->view;
106: }
107:
108: 109: 110: 111: 112:
113: protected function cachePath()
114: {
115: if (!$this->cachePath)
116: {
117: $compile = $this->configure->compile();
118: $pathCache = $compile . dirname($this->view());
119: $path = $this->configure->createDirectory($pathCache);
120:
121: $this->cachePath = $path . basename($this->view()) . '.php';
122: }
123:
124: return $this->cachePath;
125: }
126:
127: 128: 129: 130: 131:
132: protected function saveCache()
133: {
134: $compile = $this->compile();
135: $view = $this->viewPath;
136:
137: if (file_exists($view))
138: {
139: return file_put_contents($this->cachePath(), $compile);
140: }
141:
142: throw new \InvalidArgumentException('File not found ' . $view);
143: }
144:
145: 146: 147:
148: protected function curView()
149: {
150: if (!$this->curView)
151: {
152: $this->curView = file_get_contents($this->viewPath);
153: }
154:
155: return $this->curView;
156: }
157:
158: 159: 160: 161: 162: 163: 164:
165: protected function lexer($compile)
166: {
167: return $this->configure->tokenizer()->lexer($compile);
168: }
169:
170: 171: 172: 173: 174:
175: protected function removeComments($view)
176: {
177: return preg_replace('~{*(.*?)\*}([\s]+)?~', '', $view);
178: }
179:
180: 181: 182: 183: 184:
185: protected function removePhpTags($view)
186: {
187: $view = preg_replace('~(<\?php)~', '<!-- ', $view);
188: $view = preg_replace('~(<\?=)~', '<!-- ', $view);
189: $view = preg_replace('~(<\?)~', '<!-- ', $view);
190:
191: return preg_replace('~(\?>)~', ' -->', $view);
192: }
193:
194: protected function literal($view)
195: {
196: return preg_replace_callback('~{literal\s*}((\n|.)*){/literal}~', function ($matches)
197: {
198: $literal = new Extension\TLiteral\TLiteral($this, $this->configure, [$matches[1]]);
199:
200: return $literal->view();
201: }, $view);
202: }
203:
204: 205: 206: 207: 208: 209: 210:
211: protected function token($command)
212: {
213: return $this->configure->tokenizer()
214: ->commandLexer($this, $this->configure, $command);
215: }
216:
217: 218: 219: 220: 221:
222: protected function quote($command)
223: {
224: return '~' . preg_quote($command, null) . '~';
225: }
226:
227: 228: 229: 230: 231: 232: 233: 234:
235: protected function replace($command, $data, $text, $limit = -1)
236: {
237: return preg_replace($this->quote($command), $data, $text, $limit);
238: }
239:
240: 241: 242: 243: 244: 245: 246:
247: protected function replaceOne($command, $data, $text)
248: {
249: return $this->replace($command, $data, $text, 1);
250: }
251:
252: 253: 254: 255: 256:
257: protected function compile()
258: {
259: $compile = $this->removeComments($this->curView());
260: $compile = $this->removePhpTags($compile);
261: $compile = $this->literal($compile);
262:
263: foreach ($this->lexer($compile) as $command)
264: {
265: $compile = $this->replaceOne(
266: '{' . $command . '}',
267: $this->token($command),
268: $compile
269: );
270: }
271:
272: return $compile;
273: }
274:
275: 276: 277: 278: 279:
280: public function __get($name)
281: {
282: return $this->configure->getVariable($name);
283: }
284:
285: 286: 287: 288:
289: public function __set($name, $value)
290: {
291: $this->assign($name, $value);
292: }
293:
294: 295: 296: 297:
298: public function assign($name, $value)
299: {
300: $this->configure->setVariable($name, $value);
301: }
302:
303: 304: 305: 306: 307:
308: public function __isset($name)
309: {
310: return $this->configure->issetVariable($name);
311: }
312:
313: 314: 315: 316: 317: 318: 319:
320: public function checkView($view)
321: {
322: $this->view($view);
323:
324: if ($this->configure->isDebug() ||
325: !file_exists($this->cachePath()) ||
326: filemtime($this->viewPath) > filemtime($this->cachePath())
327: )
328: {
329: $this->saveCache();
330: }
331:
332: return $this->cachePath();
333: }
334:
335: 336: 337: 338: 339: 340: 341:
342: public function parse($view)
343: {
344: $fullPath = $this->checkView($view);
345: $cachePath = realpath($this->configure->compile());
346:
347: $path = str_replace($cachePath, '', $fullPath);
348:
349: return ltrim($path, '/\\');
350: }
351:
352: 353: 354:
355: public function random()
356: {
357: if (function_exists('random_int'))
358: {
359: return random_int(0, PHP_INT_MAX);
360: }
361:
362: return mt_rand(0, PHP_INT_MAX);
363: }
364:
365: 366: 367: 368: 369: 370: 371:
372: public function render($view)
373: {
374: $rand = $this->random();
375:
376: ${'____parse' . $rand} = $this->checkView($view);
377: unset($view);
378:
379: extract($this->configure->getVariables(), EXTR_REFS);
380:
381: ob_start();
382: require ${'____parse' . $rand};
383:
384: ${'____result' . $rand} = ob_get_clean();
385:
386: ${'____extends' . $rand} = $this->configure->getExtendsFile($this->selectView(), true);
387:
388: foreach (${'____extends' . $rand} as ${'____extend' . $rand})
389: {
390: ${'____result' . $rand} = (new static($this->configure))
391: ->render(${'____extend' . $rand}) . ${'____result' . $rand};
392: }
393:
394: return ${'____result' . $rand};
395: }
396:
397: }