AssetConverter.php 3.68 KB
Newer Older
Qiang Xue committed
1 2 3 4 5 6 7 8 9 10 11
<?php
/**
 * @link http://www.yiiframework.com/
 * @copyright Copyright (c) 2008 Yii Software LLC
 * @license http://www.yiiframework.com/license/
 */

namespace yii\web;

use Yii;
use yii\base\Component;
12
use yii\base\Exception;
Qiang Xue committed
13 14

/**
Qiang Xue committed
15 16
 * AssetConverter supports conversion of several popular script formats into JS or CSS scripts.
 *
17 18
 * It is used by [[AssetManager]] to convert files after they have been published.
 *
Qiang Xue committed
19 20 21
 * @author Qiang Xue <qiang.xue@gmail.com>
 * @since 2.0
 */
22
class AssetConverter extends Component implements AssetConverterInterface
Qiang Xue committed
23
{
24 25 26 27 28 29 30 31 32 33 34 35 36
    /**
     * @var array the commands that are used to perform the asset conversion.
     * The keys are the asset file extension names, and the values are the corresponding
     * target script types (either "css" or "js") and the commands used for the conversion.
     */
    public $commands = [
        'less' => ['css', 'lessc {from} {to} --no-color'],
        'scss' => ['css', 'sass {from} {to}'],
        'sass' => ['css', 'sass {from} {to}'],
        'styl' => ['js', 'stylus < {from} > {to}'],
        'coffee' => ['js', 'coffee -p {from} > {to}'],
        'ts' => ['js', 'tsc --out {to} {from}'],
    ];
Qiang Xue committed
37

38 39
    /**
     * Converts a given asset file into a CSS or JS file.
40 41
     * @param string $asset the asset file path, relative to $basePath
     * @param string $basePath the directory the $asset is relative to.
42 43 44 45 46 47 48 49 50 51 52 53 54
     * @return string the converted asset file path, relative to $basePath.
     */
    public function convert($asset, $basePath)
    {
        $pos = strrpos($asset, '.');
        if ($pos !== false) {
            $ext = substr($asset, $pos + 1);
            if (isset($this->commands[$ext])) {
                list ($ext, $command) = $this->commands[$ext];
                $result = substr($asset, 0, $pos + 1) . $ext;
                if (@filemtime("$basePath/$result") < filemtime("$basePath/$asset")) {
                    $this->runCommand($command, $basePath, $asset, $result);
                }
55

56 57 58
                return $result;
            }
        }
59

60 61 62 63 64
        return $asset;
    }

    /**
     * Runs a command to convert asset files.
65 66 67 68 69
     * @param string $command the command to run
     * @param string $basePath asset base path and command working directory
     * @param string $asset the name of the asset file
     * @param string $result the name of the file to be generated by the converter command
     * @return boolean true on success, false on failure. Failures will be logged.
70
     * @throws \yii\base\Exception when the command fails and YII_DEBUG is true.
71
     * In production mode the error will be logged.
72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101
     */
    protected function runCommand($command, $basePath, $asset, $result)
    {
        $command = strtr($command, [
            '{from}' => escapeshellarg("$basePath/$asset"),
            '{to}' => escapeshellarg("$basePath/$result"),
        ]);
        $descriptor = [
            1 => ['pipe', 'w'],
            2 => ['pipe', 'w'],
        ];
        $pipes = [];
        $proc = proc_open($command, $descriptor, $pipes, $basePath);
        $stdout = stream_get_contents($pipes[1]);
        $stderr = stream_get_contents($pipes[2]);
        foreach ($pipes as $pipe) {
            fclose($pipe);
        }
        $status = proc_close($proc);

        if ($status === 0) {
            Yii::trace("Converted $asset into $result:\nSTDOUT:\n$stdout\nSTDERR:\n$stderr", __METHOD__);
        } elseif (YII_DEBUG) {
            throw new Exception("AssetConverter command '$command' failed with exit code $status:\nSTDOUT:\n$stdout\nSTDERR:\n$stderr");
        } else {
            Yii::error("AssetConverter command '$command' failed with exit code $status:\nSTDOUT:\n$stdout\nSTDERR:\n$stderr");
        }

        return $status === 0;
    }
Zander Baldwin committed
102
}