Completed
Branch 09branch (0a5c88)
by Anton
05:50
created

StemplerEngine::wrapLoader()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 13
Code Lines 8

Duplication

Lines 13
Ratio 100 %

Importance

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