Completed
Branch 09branch (31301e)
by Anton
02:42
created

StemplerEngine::compile()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 48
Code Lines 28

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 28
nc 4
nop 2
dl 0
loc 48
rs 9.125
c 0
b 0
f 0
1
<?php
2
/**
3
 * spiral
4
 *
5
 * @author    Wolfy-J
6
 */
7
8
namespace Spiral\Views\Engines;
9
10
use Spiral\Core\ContainerInterface;
11
use Spiral\Debug\Traits\BenchmarkTrait;
12
use Spiral\Files\FilesInterface;
13
use Spiral\Stempler\Supervisor;
14
use Spiral\Stempler\Syntaxes\DarkSyntax;
15
use Spiral\Views\EngineInterface;
16
use Spiral\Views\Engines\Prototypes\AbstractEngine;
17
use Spiral\Views\Engines\Stempler\LoaderBridge;
18
use Spiral\Views\Engines\Stempler\StemplerCache;
19
use Spiral\Views\Engines\Stempler\StemplerView;
20
use Spiral\Views\EnvironmentInterface;
21
use Spiral\Views\LoaderInterface;
22
use Spiral\Views\Loaders\ModifiableLoader;
23
use Spiral\Views\ProcessorInterface;
24
use Spiral\Views\ViewInterface;
25
use Spiral\Views\ViewSource;
26
27
/**
28
 * Spiral Stempler template composer.
29
 */
30
class StemplerEngine extends AbstractEngine
31
{
32
    use BenchmarkTrait;
33
34
    /**
35
     * Container is needed to provide proper scope isolation at moment of rendering.
36
     *
37
     * @var ContainerInterface
38
     */
39
    protected $container;
40
41
    /**
42
     * @var StemplerCache
43
     */
44
    protected $cache;
45
46
    /**
47
     * To be applied to view source before it's being loaded.
48
     *
49
     * @var array
50
     */
51
    protected $modifiers = [];
52
53
    /**
54
     * To be applied to already compiled view source.
55
     *
56
     * @var array
57
     */
58
    protected $processors = [];
59
60
    /**
61
     * @param EnvironmentInterface $environment
62
     * @param LoaderInterface      $loader
63
     * @param FilesInterface       $files
64
     * @param ContainerInterface   $container
65
     * @param array                $modifiers
66
     * @param array                $processors
67
     */
68
    public function __construct(
69
        EnvironmentInterface $environment,
70
        LoaderInterface $loader,
71
        FilesInterface $files,
72
        ContainerInterface $container,
73
        array $modifiers = [],
74
        array $processors = []
75
    ) {
76
        parent::__construct($environment, $loader);
77
78
        $this->container = $container;
79
80
        $this->modifiers = $modifiers;
81
        $this->processors = $processors;
82
83
        $this->cache = new StemplerCache($this->environment, $files);
84
    }
85
86
    /**
87
     * {@inheritdoc}
88
     */
89
    public function get(string $path): ViewInterface
90
    {
91
        return new StemplerView(
92
            $this->compile($path),
93
            $this->container
94
        );
95
    }
96
97
    /**
98
     * {@inheritdoc}
99
     *
100
     * @return ViewSource Points to compiled view source.
101
     */
102
    public function compile(string $path, bool $reset = false): ViewSource
103
    {
104
        $context = $this->loader->getSource($path);
105
        $cached = new ViewSource(
106
            $this->cache->cacheFilename($path),
107
            $context->getName(),
108
            $context->getNamespace()
109
        );
110
111
        if (
112
            $context->isFresh($this->cache->timeCached($cached->getFilename()))
113
            && !$reset
114
        ) {
115
            //Compiled and cached and not changed (simple cache check)
116
            return $cached;
117
        }
118
119
        $benchmark = $this->benchmark('compile', $path);
120
        try {
121
            //Compiling into cache
122
            $cached = $cached->withCode(
123
                $this->createSupervisor()->createNode($path)->compile()
124
            );
125
        } finally {
126
            $this->benchmark($benchmark);
127
        }
128
129
        $benchmark = $this->benchmark('cache', $path);
130
        try {
131
            //To simplify processors life let's write file to cache first (this might help in debugging)
132
            $this->cache->write(
133
                $cached->getFilename(),
134
                $cached->getCode()
135
            );
136
137
            //Ok, now we can apply processors
138
            $cached = $this->postProcess($cached);
139
140
            $this->cache->write(
141
                $cached->getFilename(),
142
                $cached->getCode()
143
            );
144
        } finally {
145
            $this->benchmark($benchmark);
146
        }
147
148
        return $cached;
149
    }
150
151
    /**
152
     * {@inheritdoc}
153
     *
154
     * @return $this
155
     */
156
    public function withEnvironment(EnvironmentInterface $environment): EngineInterface
157
    {
158
        /**
159
         * @var self $engine
160
         */
161
        $engine = parent::withEnvironment($environment);
162
        $engine->cache = $engine->cache->withEnvironment($environment);
163
164
        return $engine;
165
    }
166
167
    /**
168
     * Create new instance of supervisor.
169
     *
170
     * @return Supervisor
171
     */
172
    protected function createSupervisor(): Supervisor
173
    {
174
        //Prepare loader
175
        return new Supervisor($this->wrapLoader(), new DarkSyntax());
176
    }
177
178
    /**
179
     * Run associated processors for post-processing.
180
     *
181
     * @param \Spiral\Views\ViewSource $source
182
     *
183
     * @return ViewSource
184
     */
185
    protected function postProcess(ViewSource $source): ViewSource
186
    {
187
        foreach ($this->processors as $processor) {
188
            /**
189
             * @var ProcessorInterface
190
             */
191
            if (!is_object($processor)) {
192
                $processor = $this->container->make($processor);
193
            }
194
195
            $benchmark = $this->benchmark(
196
                'process',
197
                get_class($processor) . '-{' . $source->getName()
198
            );
199
200
            $source->getCode();
201
202
            try {
203
                //Post processing
204
                $source = $source->withCode(
205
                    $processor->modify($this->environment, $source, $source->getCode())
206
                );
207
            } finally {
208
                $this->benchmark($benchmark);
209
            }
210
        }
211
212
        return $source;
213
    }
214
215
    /**
216
     * In most of cases stempler will get view processors to be executed before composing, to run
217
     * such processors we need special wrapper at top of environment.
218
     *
219
     * @return \Spiral\Stempler\LoaderInterface
220
     */
221 View Code Duplication
    private function wrapLoader(): \Spiral\Stempler\LoaderInterface
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
222
    {
223
        $processors = [];
224
        foreach ($this->modifiers as $modifier) {
225
            if (is_object($modifier)) {
226
                $processors[] = $modifier;
227
            } else {
228
                $processors[] = $this->container->make($modifier);
229
            }
230
        }
231
232
        return new LoaderBridge(
233
            new ModifiableLoader($this->environment, $this->loader, $processors)
234
        );
235
    }
236
}