Completed
Push — master ( ff2012...83cef1 )
by Igor
02:43
created

Native::enableCache()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 0
1
<?php
2
/**
3
 * @license MIT
4
 * @author Igor Sorokin <[email protected]>
5
 */
6
namespace Dspbee\Bundle\Template;
7
8
use Dspbee\Bundle\Common\TFileSystem;
9
use Dspbee\Bundle\Debug\Wrap;
10
use Dspbee\Bundle\Template\Exception\FileNotFoundException;
11
use Dspbee\Core\Request;
12
13
/**
14
 * Class Native
15
 * @package Dspbee\Bundle\Template
16
 */
17
class Native
18
{
19
    use TFileSystem;
20
21
    /**
22
     * @param string $packageRoot
23
     * @param Request|null $request
24
     * @param bool $cache
25
     */
26
    public function __construct($packageRoot, Request $request = null, $cache = true)
27
    {
28
        $this->packageRoot = rtrim($packageRoot, '/');
29
        $this->request = $request;
30
        $this->routeView = false;
31
32
        if (class_exists('Dspbee\Bundle\Debug\Wrap')) {
33
            $this->cache = !Wrap::$debugEnabled;
34
        } else {
35
            $this->cache = $cache;
36
        }
37
    }
38
39
    /**
40
     * Create content from template and data.
41
     *
42
     * @param string $name
43
     * @param array $data
44
     *
45
     * @return string|null
46
     */
47
    public function getContent($name, array $data = [])
48
    {
49
        if (false === strpos($name, '.')) {
50
            if (!empty($name)) {
51
                $name = '.' . $name;
52
            }
53
            $name = 'view' . $name . '.html.php';
54
            $this->routeView = true;
55
        }
56
        $path = $this->packageRoot . '/view/_cache/' . str_replace('/', '_', $name);
57
58
        if (!file_exists($path) || !$this->cache) {
59
            $code = $this->compile($name, true, true);
60
            if (empty($code)) {
61
                return null;
62
            }
63
64
            $fh = fopen($path, 'wb');
65
            if (flock($fh, LOCK_EX)) {
66
                fwrite($fh, $code);
67
                flock($fh, LOCK_UN);
68
            }
69
            fflush($fh);
70
            fclose($fh);
71
        }
72
73
        $fh = fopen($path, 'rb');
74
        flock($fh, LOCK_SH);
75
76
        if (null !== $this->request) {
77
            $data = array_replace($data, ['request' => $this->request]);
78
        }
79
80
        $html = self::renderTemplate($path, $data);
81
82
        flock($fh, LOCK_UN);
83
        fclose($fh);
84
85
        return $html;
86
    }
87
88
    /**
89
     * Use cache.
90
     */
91
    public function enableCache()
92
    {
93
        $this->cache = true;
94
    }
95
96
    /**
97
     * Render without cache.
98
     */
99
    public function disableCache()
100
    {
101
        $this->cache = false;
102
    }
103
104
    /**
105
     * Delete all cached templates.
106
     */
107
    public function clearCache()
108
    {
109
        self::removeFromDir($this->packageRoot . '/view/_cache');
110
    }
111
112
    /**
113
     * Create solid template.
114
     *
115
     * @param string $name
116
     * @param bool $processInclude
117
     * @param bool $processExtends
118
     *
119
     * @return string|null
120
     *
121
     * @throws FileNotFoundException
122
     */
123
    private function compile($name, $processInclude, $processExtends)
124
    {
125
        if ($this->routeView) {
126
            $this->routeView = false;
127
            $path = '';
128
            $stack = debug_backtrace();
129
            foreach ($stack as $item) {
130
                if (false !== stripos($item['file'], '\\Route\\')) {
131
                    $path = pathinfo($item['file'], PATHINFO_DIRNAME) . '/' . $name;
132
                    break;
133
                }
134
            }
135
        } else {
136
            $path = $this->packageRoot . '/view/' . $name;
137
        }
138
139
        if (file_exists($path)) {
140
            ob_start();
141
            readfile($path);
142
            $code = ob_get_clean();
143
        } else {
144
            throw new FileNotFoundException($path);
145
        }
146
147
        if ($processInclude) {
148
            preg_match_all('/<!-- include (.*) -->/', $code, $matchList);
149
            if (count($matchList)) {
150
                foreach ($matchList[1] as $key => $template) {
151
                    if (!empty($matchList[0][$key]) && false !== strpos($code, $matchList[0][$key])) {
152
                        $template = trim($template);
153
                        $code = str_replace($matchList[0][$key], $this->compile($template, true, false), $code);
154
                    }
155
                }
156
            }
157
        }
158
159
        if ($processExtends) {
160
            preg_match_all('/<!-- extends (.*) -->/', $code, $matchList);
161
            if (isset($matchList[1][0])) {
162
                $template = trim($matchList[1][0]);
163
                $parentHtml = $this->compile($template, true, false);
164
165
                $code = str_replace($matchList[0][0], '', $code);
166
                $parentHtml = str_replace('<!-- section -->', $code, $parentHtml);
167
                $code = $parentHtml;
168
            }
169
        }
170
171
        return $code;
172
    }
173
174
    /**
175
     * Safe include. Used for scope isolation.
176
     *
177
     * @param string $__file__  File to include
178
     * @param array  $data      Data passed to template
179
     *
180
     * @return string
181
     */
182
    private static function renderTemplate($__file__, array $data)
183
    {
184
        ob_start();
185
        extract($data);
186
        include $__file__;
187
        return ob_get_clean();
188
    }
189
190
    private $request;
191
    private $packageRoot;
192
    private $cache;
193
    private $routeView;
194
}
195
196