Passed
Pull Request — master (#15)
by Thijs
01:46
created

ScssAssetConverter::getCssAsset()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 7
ccs 5
cts 5
cp 1
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 5
nc 1
nop 2
crap 1
1
<?php
2
3
namespace lucidtaz\yii2scssphp;
4
5
use Leafo\ScssPhp\Compiler;
6
use lucidtaz\yii2scssphp\storage\FsStorage;
7
use lucidtaz\yii2scssphp\storage\Storage;
8
use RuntimeException;
9
use Yii;
10
use yii\base\Component;
11
use yii\web\AssetConverterInterface;
12
13
class ScssAssetConverter extends Component implements AssetConverterInterface
14
{
15
    /**
16
     * @var Storage
17
     */
18
    public $storage;
19
20
    /**
21
     * @var boolean whether the source asset file should be converted even if
22
     * its result already exists. You may want to set this to be `true` during
23
     * the development stage to make sure the converted assets are always up-to-
24
     * date. Do not set this to true on production servers as it will
25
     * significantly degrade the performance.
26
     */
27
    public $forceConvert = false;
28
29
    /**
30
     * @var Compiler|mixed The mixed type hint is added to keep PHPStan happy
31
     * below PHP 7.2, since Yii was annotated to return "object" which confuses
32
     * PHPStan < PHP 7.2
33
     */
34
    private $compiler;
35
36 14
    public function init()
37
    {
38 14
        parent::init();
39 14
        if (!isset($this->storage)) {
40 1
            $this->storage = new FsStorage;
41
        }
42 14
        $this->compiler = Yii::createObject(Compiler::class);
43 14
    }
44
45
    /**
46
     * Converts a given SCSS asset file into a CSS file.
47
     * @param string $asset the asset file path, relative to $basePath
48
     * @param string $basePath the directory the $asset is relative to.
49
     * @return string the converted asset file path, relative to $basePath.
50
     */
51 13
    public function convert($asset, $basePath)
52
    {
53 13
        $extension = $this->getExtension($asset);
54 13
        if ($extension !== 'scss') {
55 2
            return $asset;
56
        }
57 11
        $cssAsset = $this->getCssAsset($asset, 'css');
58
59 11
        $inFile = "$basePath/$asset";
60 11
        $outFile = "$basePath/$cssAsset";
61
        
62 11
        $this->compiler->setImportPaths(dirname($inFile));
63
64 11
        if (!$this->storage->exists($inFile)) {
65 1
            Yii::error("Input file $inFile not found.", __METHOD__);
66 1
            return $asset;
67
        }
68
69 10
        $this->convertAndSaveIfNeeded($inFile, $outFile);
70
71 10
        return $cssAsset;
72
    }
73
74 13
    private function getExtension(string $filename): string
75
    {
76 13
        return pathinfo($filename, PATHINFO_EXTENSION);
77
    }
78
79
    /**
80
     * Get the relative path and filename of the asset
81
     * @param string $filename e.g. path/asset.css
82
     * @param string $newExtension e.g. scss
83
     * @return string e.g. path/asset.scss
84
     */
85 9
    protected function getCssAsset(string $filename, string $newExtension): string
86
    {
87 9
        $extensionlessFilename = pathinfo($filename, PATHINFO_FILENAME);
88 9
        $filenamePosition = strrpos($filename, $extensionlessFilename);
89 9
        $relativePath = substr($filename, 0, $filenamePosition);
90 9
        return "$relativePath$extensionlessFilename.$newExtension";
91
    }
92
93 10
    private function convertAndSaveIfNeeded(string $inFile, string $outFile)
94
    {
95 10
        if ($this->shouldConvert($inFile, $outFile)) {
96 9
            $css = $this->compiler->compile($this->storage->get($inFile), $inFile);
97 9
            $this->storage->put($outFile, $css);
98
        }
99 10
    }
100
101 10
    private function shouldConvert(string $inFile, string $outFile): bool
102
    {
103 10
        if (!$this->storage->exists($outFile)) {
104 6
            return true;
105
        }
106 4
        if ($this->forceConvert) {
107 1
            return true;
108
        }
109
        try {
110 3
            return $this->isOlder($outFile, $inFile);
111 1
        } catch (RuntimeException $e) {
112 1
            Yii::warning('Encountered RuntimeException message "' . $e->getMessage() . '", going to convert.', __METHOD__);
113 1
            return true;
114
        }
115
    }
116
117 3
    private function isOlder(string $fileA, string $fileB): bool
118
    {
119 3
        return $this->storage->getMtime($fileA) < $this->storage->getMtime($fileB);
120
    }
121
}
122