Issues (902)

framework/web/AssetConverter.php (1 issue)

Labels
Severity
1
<?php
2
/**
3
 * @link https://www.yiiframework.com/
4
 * @copyright Copyright (c) 2008 Yii Software LLC
5
 * @license https://www.yiiframework.com/license/
6
 */
7
8
namespace yii\web;
9
10
use Yii;
11
use yii\base\Component;
12
use yii\base\Exception;
13
14
/**
15
 * AssetConverter supports conversion of several popular formats into JS or CSS files.
16
 *
17
 * It is used by [[AssetManager]] to convert files after they have been published.
18
 *
19
 * @author Qiang Xue <[email protected]>
20
 * @since 2.0
21
 */
22
class AssetConverter extends Component implements AssetConverterInterface
23
{
24
    /**
25
     * @var array the commands that are used to perform the asset conversion.
26
     * The keys are the asset file extension names, and the values are the corresponding
27
     * target script types (either "css" or "js") and the commands used for the conversion.
28
     *
29
     * The command placeholders: `{from}` - source file, `{to}` - converted file.
30
     *
31
     * You may also use a [path alias](guide:concept-aliases) to specify the location of the command:
32
     *
33
     * ```php
34
     * [
35
     *     'styl' => ['css', '@app/node_modules/bin/stylus < {from} > {to}'],
36
     * ]
37
     * ```
38
     *
39
     * Note: `Yii::getAlias()` can replace alias at the begin of the command only.
40
     *
41
     * @see https://sass-lang.com/documentation/cli/ SASS/SCSS
42
     */
43
    public $commands = [
44
        'less' => ['css', 'lessc {from} {to} --no-color --source-map'],
45
        'scss' => ['css', 'sass --style=compressed {from} {to}'],
46
        'sass' => ['css', 'sass --style=compressed {from} {to}'],
47
        'styl' => ['css', 'stylus < {from} > {to}'],
48
        'coffee' => ['js', 'coffee -p {from} > {to}'],
49
        'ts' => ['js', 'tsc --out {to} {from}'],
50
    ];
51
    /**
52
     * @var bool whether the source asset file should be converted even if its result already exists.
53
     * You may want to set this to be `true` during the development stage to make sure the converted
54
     * assets are always up-to-date. Do not set this to true on production servers as it will
55
     * significantly degrade the performance.
56
     */
57
    public $forceConvert = false;
58
59
60
    /**
61
     * Converts a given asset file into a CSS or JS file.
62
     * @param string $asset the asset file path, relative to $basePath
63
     * @param string $basePath the directory the $asset is relative to.
64
     * @return string the converted asset file path, relative to $basePath.
65
     */
66 34
    public function convert($asset, $basePath)
67
    {
68 34
        $pos = strrpos($asset, '.');
69 34
        if ($pos !== false) {
70 34
            $ext = substr($asset, $pos + 1);
71 34
            if (isset($this->commands[$ext])) {
72 2
                list($ext, $command) = $this->commands[$ext];
73 2
                $result = substr($asset, 0, $pos + 1) . $ext;
74 2
                if ($this->forceConvert || @filemtime("$basePath/$result") < @filemtime("$basePath/$asset")) {
75 2
                    $this->runCommand($command, $basePath, $asset, $result);
76
                }
77
78 2
                return $result;
79
            }
80
        }
81
82 32
        return $asset;
83
    }
84
85
    /**
86
     * Runs a command to convert asset files.
87
     * @param string $command the command to run. If prefixed with an `@` it will be treated as a [path alias](guide:concept-aliases).
88
     * @param string $basePath asset base path and command working directory
89
     * @param string $asset the name of the asset file
90
     * @param string $result the name of the file to be generated by the converter command
91
     * @return bool true on success, false on failure. Failures will be logged.
92
     * @throws \yii\base\Exception when the command fails and YII_DEBUG is true.
93
     * In production mode the error will be logged.
94
     */
95 2
    protected function runCommand($command, $basePath, $asset, $result)
96
    {
97 2
        $command = Yii::getAlias($command);
98
99 2
        $command = strtr($command, [
0 ignored issues
show
It seems like $command can also be of type false; however, parameter $str of strtr() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

99
        $command = strtr(/** @scrutinizer ignore-type */ $command, [
Loading history...
100 2
            '{from}' => escapeshellarg("$basePath/$asset"),
101 2
            '{to}' => escapeshellarg("$basePath/$result"),
102 2
        ]);
103 2
        $descriptor = [
104 2
            1 => ['pipe', 'w'],
105 2
            2 => ['pipe', 'w'],
106 2
        ];
107 2
        $pipes = [];
108 2
        $proc = proc_open($command, $descriptor, $pipes, $basePath);
109 2
        $stdout = stream_get_contents($pipes[1]);
110 2
        $stderr = stream_get_contents($pipes[2]);
111 2
        foreach ($pipes as $pipe) {
112 2
            fclose($pipe);
113
        }
114 2
        $status = proc_close($proc);
115
116 2
        if ($status === 0) {
117 2
            Yii::debug("Converted $asset into $result:\nSTDOUT:\n$stdout\nSTDERR:\n$stderr", __METHOD__);
118
        } elseif (YII_DEBUG) {
119
            throw new Exception("AssetConverter command '$command' failed with exit code $status:\nSTDOUT:\n$stdout\nSTDERR:\n$stderr");
120
        } else {
121
            Yii::error("AssetConverter command '$command' failed with exit code $status:\nSTDOUT:\n$stdout\nSTDERR:\n$stderr", __METHOD__);
122
        }
123
124 2
        return $status === 0;
125
    }
126
}
127