253 lines
6.6 KiB
PHP
253 lines
6.6 KiB
PHP
<?php
|
|
|
|
namespace sacy;
|
|
|
|
abstract class ExternalProcessor
|
|
{
|
|
abstract protected function getCommandLine($filename, $opts = []);
|
|
|
|
public function transform($in, $filename, $opts = [])
|
|
{
|
|
$s = [
|
|
0 => ['pipe', 'r'],
|
|
1 => ['pipe', 'w'],
|
|
2 => ['pipe', 'w'],
|
|
];
|
|
$cmd = $this->getCommandLine($filename, $opts);
|
|
$p = proc_open($cmd, $s, $pipes, getcwd());
|
|
if (!is_resource($p)) {
|
|
throw new \Exception("Failed to execute {$cmd}");
|
|
}
|
|
|
|
fwrite($pipes[0], $in);
|
|
fclose($pipes[0]);
|
|
|
|
$out = stream_get_contents($pipes[1]);
|
|
$err = stream_get_contents($pipes[2]);
|
|
|
|
fclose($pipes[1]);
|
|
fclose($pipes[2]);
|
|
|
|
$r = proc_close($p);
|
|
|
|
if ($r != 0) {
|
|
throw new \Exception("Command returned {$r}: {$out} {$err}");
|
|
}
|
|
|
|
return $out;
|
|
}
|
|
}
|
|
|
|
class ExternalProcessorRegistry
|
|
{
|
|
private static $transformers;
|
|
private static $compressors;
|
|
|
|
public static function registerTransformer($type, $cls)
|
|
{
|
|
self::$transformers[$type] = $cls;
|
|
}
|
|
|
|
public static function registerCompressor($type, $cls)
|
|
{
|
|
self::$compressors[$type] = $cls;
|
|
}
|
|
|
|
private static function lookup($type, $in)
|
|
{
|
|
return (isset($in[$type])) ? new $in[$type]() : null;
|
|
}
|
|
|
|
public static function typeIsSupported($type)
|
|
{
|
|
return isset(self::$transformers[$type])
|
|
|| isset(self::$compressors[$type]);
|
|
}
|
|
|
|
/**
|
|
* @static
|
|
*
|
|
* @param $type string mime type of input
|
|
*
|
|
* @return ExternalProcessor
|
|
*/
|
|
public static function getTransformerForType($type)
|
|
{
|
|
return self::lookup($type, self::$transformers);
|
|
}
|
|
|
|
/**
|
|
* @static
|
|
*
|
|
* @param $type string mime type of input
|
|
*
|
|
* @return ExternalProcessor
|
|
*/
|
|
public static function getCompressorForType($type)
|
|
{
|
|
return self::lookup($type, self::$compressors);
|
|
}
|
|
}
|
|
|
|
class ProcessorUglify extends ExternalProcessor
|
|
{
|
|
protected function getCommandLine($filename, $opts = [])
|
|
{
|
|
if (!is_executable(SACY_COMPRESSOR_UGLIFY)) {
|
|
throw new Exception('SACY_COMPRESSOR_UGLIFY defined but not executable');
|
|
}
|
|
|
|
return SACY_COMPRESSOR_UGLIFY;
|
|
}
|
|
}
|
|
|
|
class ProcessorCoffee extends ExternalProcessor
|
|
{
|
|
protected function getCommandLine($filename, $opts = [])
|
|
{
|
|
if (!is_executable(SACY_TRANSFORMER_COFFEE)) {
|
|
throw new Exception('SACY_TRANSFORMER_COFFEE defined but not executable');
|
|
}
|
|
|
|
return sprintf('%s -c -s', SACY_TRANSFORMER_COFFEE);
|
|
}
|
|
}
|
|
|
|
class ProcessorEco extends ExternalProcessor
|
|
{
|
|
protected function getType()
|
|
{
|
|
return 'text/x-eco';
|
|
}
|
|
|
|
protected function getCommandLine($filename, $opts = [])
|
|
{
|
|
if (!is_executable(SACY_TRANSFORMER_ECO)) {
|
|
throw new Exception('SACY_TRANSFORMER_ECO defined but not executable');
|
|
}
|
|
// Calling eco with the filename here. Using stdin wouldn't
|
|
// cut it, as eco uses the filename to figure out the name of
|
|
// the js function it outputs.
|
|
$eco_root = $opts['eco-root'];
|
|
|
|
return sprintf('%s %s -p %s',
|
|
SACY_TRANSFORMER_ECO,
|
|
$eco_root ? sprintf('-i %s', escapeshellarg($eco_root)) : '',
|
|
escapeshellarg($filename)
|
|
);
|
|
}
|
|
}
|
|
|
|
class ProcessorSass extends ExternalProcessor
|
|
{
|
|
protected function getType()
|
|
{
|
|
return 'text/x-sass';
|
|
}
|
|
|
|
protected function getCommandLine($filename, $opts = [])
|
|
{
|
|
if (!is_executable(SACY_TRANSFORMER_SASS)) {
|
|
throw new Exception('SACY_TRANSFORMER_SASS defined but not executable');
|
|
}
|
|
$libpath = $opts['library_path'] ?: [dirname($filename)];
|
|
$libpath[] = $_SERVER['DOCUMENT_ROOT'] ?: getcwd();
|
|
|
|
$path =
|
|
implode(' ', array_map(function ($p) {
|
|
return '-I '.escapeshellarg($p);
|
|
}, array_unique($libpath)));
|
|
|
|
return sprintf('%s --cache-location=%s -s %s %s',
|
|
SACY_TRANSFORMER_SASS,
|
|
escapeshellarg(sys_get_temp_dir()),
|
|
$this->getType() == 'text/x-scss' ? '--scss' : '',
|
|
$path
|
|
);
|
|
}
|
|
}
|
|
|
|
class ProcessorScss extends ProcessorSass
|
|
{
|
|
protected function getType()
|
|
{
|
|
return 'text/x-scss';
|
|
}
|
|
}
|
|
|
|
class ProcessorLess extends ExternalProcessor
|
|
{
|
|
protected function getCommandLine($filename, $opts = [])
|
|
{
|
|
if (!is_executable(SACY_TRANSFORMER_LESS)) {
|
|
throw new \Exception('SACY_TRANSFORMER_LESS defined but not executable');
|
|
}
|
|
|
|
return sprintf(
|
|
'%s -I%s -',
|
|
SACY_TRANSFORMER_LESS,
|
|
escapeshellarg(dirname($filename))
|
|
);
|
|
}
|
|
}
|
|
|
|
class ProcessorJSX extends ExternalProcessor
|
|
{
|
|
protected function getCommandLine($filename, $opts = [])
|
|
{
|
|
if (!is_executable(SACY_TRANSFORMER_JSX)) {
|
|
throw new Exception('SACY_TRANSFORMER_JSX defined but not executable');
|
|
}
|
|
|
|
return SACY_TRANSFORMER_JSX;
|
|
}
|
|
}
|
|
|
|
class ProcessorFontIcons extends ExternalProcessor
|
|
{
|
|
protected function getCommandLine($filename, $opts = [])
|
|
{
|
|
return SACY_TRANSFORMER_FONTICONS." compile 'templates/icons/' -F -c {$filename}";
|
|
}
|
|
|
|
public function transform($in, $filename, $opts = [])
|
|
{
|
|
$css = 'data/tmp/cache/icons.css';
|
|
|
|
if (@filemtime($filename) > @filemtime($css)) {
|
|
parent::transform($in, $filename, $opts);
|
|
}
|
|
|
|
return str_replace('./icons', '/data/tmp/cache/icons', file_get_contents($css));
|
|
}
|
|
}
|
|
|
|
if (defined('SACY_COMPRESSOR_UGLIFY')) {
|
|
ExternalProcessorRegistry::registerCompressor('text/javascript', 'sacy\ProcessorUglify');
|
|
}
|
|
|
|
if (defined('SACY_TRANSFORMER_COFFEE')) {
|
|
ExternalProcessorRegistry::registerTransformer('text/coffeescript', 'sacy\ProcessorCoffee');
|
|
}
|
|
|
|
if (defined('SACY_TRANSFORMER_ECO')) {
|
|
ExternalProcessorRegistry::registerTransformer('text/x-eco', 'sacy\ProcessorEco');
|
|
}
|
|
|
|
if (defined('SACY_TRANSFORMER_SASS')) {
|
|
ExternalProcessorRegistry::registerTransformer('text/x-sass', 'sacy\ProcessorSass');
|
|
ExternalProcessorRegistry::registerTransformer('text/x-scss', 'sacy\ProcessorScss');
|
|
}
|
|
|
|
if (defined('SACY_TRANSFORMER_FONTICONS')) {
|
|
ExternalProcessorRegistry::registerTransformer('image/svg+xml', 'sacy\ProcessorFontIcons');
|
|
}
|
|
|
|
if (defined('SACY_TRANSFORMER_LESS')) {
|
|
ExternalProcessorRegistry::registerTransformer('text/x-less', 'sacy\ProcessorLess');
|
|
}
|
|
|
|
if (defined('SACY_TRANSFORMER_JSX')) {
|
|
ExternalProcessorRegistry::registerTransformer('text/x-jsx', 'sacy\ProcessorJSX');
|
|
}
|