Passed
Push — twig ( abd997...15b4f3 )
by Arnaud
08:11
created

Asset::compile()   B

Complexity

Conditions 6
Paths 5

Size

Total Lines 39
Code Lines 26

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 6
eloc 26
nc 5
nop 0
dl 0
loc 39
rs 8.8817
c 0
b 0
f 0
1
<?php
2
/**
3
 * This file is part of the Cecil/Cecil package.
4
 *
5
 * Copyright (c) Arnaud Ligny <[email protected]>
6
 *
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 */
10
11
namespace Cecil\Assets;
12
13
use Cecil\Builder;
14
use Cecil\Config;
15
use Cecil\Exception\Exception;
16
use Cecil\Util;
17
use MatthiasMullie\Minify;
18
use ScssPhp\ScssPhp\Compiler;
19
20
class Asset implements \ArrayAccess
21
{
22
    /** @var Builder */
23
    protected $builder;
24
    /** @var Config */
25
    protected $config;
26
    /** @var array */
27
    protected $data = [];
28
29
    /**
30
     * Loads a file.
31
     *
32
     * @param Builder
33
     * @param string     $path
34
     * @param array|null $options
35
     */
36
    public function __construct(Builder $builder, string $path, array $options = null)
37
    {
38
        $this->builder = $builder;
39
        $this->config = $builder->getConfig();
40
41
        if (false === $filePath = $this->findFile($path)) {
42
            throw new Exception(sprintf('Asset file "%s" doesn\'t exist.', $path));
43
        }
44
45
        // handles options
46
        $attributes = null; // html attributes
47
        extract(is_array($options) ? $options : [], EXTR_IF_EXISTS);
48
49
        // set data
50
        $this->data['file'] = $filePath;
51
        $this->data['path'] = '/'.ltrim($path, '/');
52
        $this->data['ext'] = pathinfo($filePath, PATHINFO_EXTENSION);
53
        $this->data['type'] = explode('/', mime_content_type($filePath))[0];
54
        $this->data['content'] = '';
55
        if ($this->data['type'] == 'text') {
56
            $this->data['content'] = file_get_contents($filePath);
57
        }
58
        $this->data['attributes'] = $attributes;
59
    }
60
61
    /**
62
     * @return string
63
     */
64
    public function __toString(): string
65
    {
66
        return $this->data['path'];
67
    }
68
69
    /**
70
     * Compiles a SCSS.
71
     *
72
     * @throws Exception
73
     *
74
     * @return self
75
     */
76
    public function compile(): self
77
    {
78
        if ($this->data['ext'] != 'scss') {
79
            throw new Exception(sprintf('Not able to compile "%s"', $this->data['path']));
80
        }
81
82
        $oldPath = $this->data['path'];
83
        $this->data['path'] = preg_replace('/scss/m', 'css', $this->data['path']);
84
        $this->data['ext'] = 'css';
85
86
        $cache = new Cache($this->builder, 'assets');
87
        $cacheKey = $cache->createKeyFromAsset($this);
88
        if (!$cache->has($cacheKey)) {
89
            $scssPhp = new Compiler();
90
            $variables = $this->config->get('assets.sass.variables') ?? [];
91
            $scssDir = $this->config->get('assets.sass.dir') ?? [];
92
            $themes = $this->config->getTheme() ?? [];
93
            foreach ($scssDir as $dir) {
94
                $scssPhp->addImportPath(Util::joinPath($this->config->getStaticPath(), $dir));
95
                $scssPhp->addImportPath(Util::joinPath(dirname($this->data['file']), $dir));
96
                foreach ($themes as $theme) {
97
                    $scssPhp->addImportPath(Util::joinPath($this->config->getThemeDirPath($theme, "static/$dir")));
98
                }
99
            }
100
            $scssPhp->setVariables($variables);
101
            $scssPhp->setFormatter('ScssPhp\ScssPhp\Formatter\\'.ucfirst($this->config->get('assets.sass.style')));
102
            $this->data['content'] = $scssPhp->compile($this->data['content']);
103
            $cache->set($cacheKey, $this->data['content']);
104
        }
105
106
        $this->data['content'] = $cache->get($cacheKey, $this->data['content']);
107
108
        // save?
109
        if (!$this->builder->getBuildOptions()['dry-run']) {
110
            Util::getFS()->dumpFile(Util::joinFile($this->config->getOutputPath(), $this->data['path']), $this->data['content']);
111
            Util::getFS()->remove(Util::joinFile($this->config->getOutputPath(), $oldPath));
112
        }
113
114
        return $this;
115
    }
116
117
    /**
118
     * Minifying a CSS or a JS.
119
     *
120
     * @throws Exception
121
     *
122
     * @return self
123
     */
124
    public function minify(): self
125
    {
126
        if ($this->data['ext'] == 'scss') {
127
            $this->compile();
128
        }
129
130
        if ($this->data['ext'] != 'css' && $this->data['ext'] != 'js') {
131
            throw new Exception(sprintf('Not able to minify "%s"', $this->data['path']));
132
        }
133
134
        $oldPath = $this->data['path'];
135
        $this->data['path'] = \sprintf('%s.min.%s', substr($this->data['path'], 0, -strlen('.'.$this->data['ext'])), $this->data['ext']);
136
137
        $cache = new Cache($this->builder, 'assets');
138
        $cacheKey = $cache->createKeyFromAsset($this);
139
        if (!$cache->has($cacheKey)) {
140
            switch ($this->data['ext']) {
141
                case 'css':
142
                    $minifier = new Minify\CSS($this->data['content']);
143
                    break;
144
                case 'js':
145
                    $minifier = new Minify\JS($this->data['content']);
146
                    break;
147
                default:
148
                    throw new Exception(sprintf('%s() error: not able to process "%s"', __FUNCTION__, $this->data));
0 ignored issues
show
Bug introduced by
$this->data of type array is incompatible with the type string expected by parameter $args of sprintf(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

148
                    throw new Exception(sprintf('%s() error: not able to process "%s"', __FUNCTION__, /** @scrutinizer ignore-type */ $this->data));
Loading history...
149
            }
150
            $this->data['content'] = $minifier->minify();
151
            $cache->set($cacheKey, $this->data['content']);
152
        }
153
        $this->data['content'] = $cache->get($cacheKey, $this->data['content']);
154
155
        // save?
156
        if (!$this->builder->getBuildOptions()['dry-run']) {
157
            Util::getFS()->dumpFile(Util::joinFile($this->config->getOutputPath(), $this->data['path']), $this->data['content']);
158
            Util::getFS()->remove(Util::joinFile($this->config->getOutputPath(), $oldPath));
159
        }
160
161
        return $this;
162
    }
163
164
    /**
165
     * Implements \ArrayAccess.
166
     */
167
    public function offsetSet($offset, $value)
168
    {
169
        if (!is_null($offset)) {
170
            $this->data[$offset] = $value;
171
        }
172
    }
173
174
    /**
175
     * Implements \ArrayAccess.
176
     */
177
    public function offsetExists($offset)
178
    {
179
        return isset($this->data[$offset]);
180
    }
181
182
    /**
183
     * Implements \ArrayAccess.
184
     */
185
    public function offsetUnset($offset)
186
    {
187
        unset($this->data[$offset]);
188
    }
189
190
    /**
191
     * Implements \ArrayAccess.
192
     */
193
    public function offsetGet($offset)
194
    {
195
        return isset($this->data[$offset]) ? $this->data[$offset] : null;
196
    }
197
198
    /**
199
     * Try to find a static file (in site or theme(s)) if exists or returns false.
200
     *
201
     * @param string $path
202
     *
203
     * @return string|false
204
     */
205
    private function findFile(string $path)
206
    {
207
        $filePath = Util::joinFile($this->config->getStaticPath(), $path);
208
        if (Util::getFS()->exists($filePath)) {
209
            return $filePath;
210
        }
211
212
        // checks in each theme
213
        foreach ($this->config->getTheme() as $theme) {
214
            $filePath = Util::joinFile($this->config->getThemeDirPath($theme, 'static'), $path);
215
            if (Util::getFS()->exists($filePath)) {
216
                return $filePath;
217
            }
218
        }
219
220
        return false;
221
    }
222
}
223