BackendBase.php in SCSS Compiler 1.0.x
Namespace
Drupal\compiler_scssFile
src/BackendBase.phpView source
<?php
namespace Drupal\compiler_scss;
use Drupal\compiler\CompilerContextInterface;
use Drupal\compiler\CompilerInputDirect;
use Drupal\compiler\CompilerInputFile;
/**
* Provides a base compiler backend implementation.
*
* Copyright (C) 2021 Library Solutions, LLC (et al.).
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* @internal
*/
abstract class BackendBase implements BackendInterface {
/**
* An array of function descriptors to be added to the compiler.
*
* @var array
*/
protected $functions = [];
/**
* Generate a list of functions that should be registered with the compiler.
*
* @see ::getCallbackFunction()
* An anonymous shim function may be introduced using this method that wraps
* the supplied callback function for additional processing of arguments and
* return values.
*
* @return \Generator
* An array of function descriptors that should be conveyed to the compiler.
*/
protected abstract function generateFunctionDescriptors() : \Generator;
/**
* Retrieve an absolute file path for the supplied input file.
*
* If the supplied context is a theme compiler context, then it will be used
* to resolve theme-relative file paths.
*
* @param \Drupal\compiler\CompilerContextInterface $context
* A compiler context used to define a compilation.
* @param string $path
* A path, possibly theme-relative.
*
* @throws \RuntimeException
* If the resulting absolute file path doesn't exist.
*
* @return string
* An absolute file path for the supplied input file.
*/
protected function getAbsoluteFilePath(CompilerContextInterface $context, string $path) {
if (($result = realpath($path)) === FALSE) {
throw new \RuntimeException('Unable to resolve file path: ' . var_export($path, TRUE));
}
return $result;
}
/**
* Fetch the callback function to use for the supplied descriptor.
*
* This method serves to provide a point where the concrete class can wrap (or
* otherwise modify) the callback function before it is conveyed to the
* underlying compiler implementation.
*
* By default, this method wraps the callback function to facilitate
* processing of its return value.
*
* @param object $descriptor
* A function descriptor object created by ::registerFunction().
*
* @return callable
* The callback function to use for the supplied descriptor.
*/
protected function getCallbackFunction(object $descriptor) {
return function ($args) use ($descriptor) {
if (count($args) !== count($descriptor->params)) {
throw new \BadFunctionCallException('PHP function parameter mismatch');
}
$args = array_map([
$this,
'processArgumentValue',
], $args);
$return = call_user_func_array($descriptor->callback, $args);
$return = $this
->processReturnValue($return);
return $return;
};
}
/**
* Attempt to retrieve the absolute import path for the compiler.
*
* An import path may be defined as a compiler option, 'import_path', whose
* value must be absolute if not passed a theme compiler context; otherwise
* the path is theme-relative.
*
* @param \Drupal\compiler\CompilerContextInterface $context
* A compiler context used to define a compilation.
*
* @return string|null
* An absolute import path for the compiler on success; NULL otherwise.
*/
protected function getImportPath(CompilerContextInterface $context) {
$options = $context
->getOptions();
if (array_key_exists('import_path', $options) && is_string($options['import_path']) && !empty($options['import_path'])) {
$result = $this
->getAbsoluteFilePath($context, $options['import_path']);
}
return isset($result) ? $result : NULL;
}
/**
* Retrieve the source code to pass to the compiler.
*
* @param \Drupal\compiler\CompilerContextInterface $context
* A compiler context used to define a compilation.
*
* @return string
* The source code to pass to the compiler.
*/
protected function getInput(CompilerContextInterface $context) {
$source = [];
// Iterate through each compiler input to construct the compiler source.
foreach (new \RecursiveIteratorIterator($context
->getInputs()) as $input) {
$result = $input
->get();
if ($input instanceof CompilerInputFile) {
$result = $this
->getAbsoluteFilePath($context, $result);
$result = file_get_contents($result);
}
elseif (!$input instanceof CompilerInputDirect) {
throw new \RuntimeException('Unsupported input type');
}
$source[] = $result;
}
// Concatenate the array of source code into a single string.
return implode("\r\n", $source);
}
/**
* Process an argument value from the compiler ahead of the callback.
*
* This method is responsible for the dynamic type juggling between the two
* languages to improve the developer experience when writing functions.
*
* @param mixed $value
* The input value to process.
*
* @return mixed
* The processed input value.
*/
public abstract function processArgumentValue($value);
/**
* Process a bridge function return value before it's passed to the compiler.
*
* This method is responsible for the dynamic type juggling between the two
* languages to improve the developer experience when writing functions.
*
* @param mixed $value
* The input value to process.
*
* @return mixed
* The processed input value.
*/
public abstract function processReturnValue($value);
/**
* {@inheritdoc}
*/
public function registerFunction(callable $callback, $name = NULL) {
// Use reflection to facilitate registering the supplied callback function.
$reflection = new \ReflectionFunction($callback);
// Attempt to resolve the name of the callback function.
if (!$reflection
->isClosure()) {
$name = $reflection
->getShortName();
}
elseif (preg_match(self::IDENT_EXPR, $name) !== 1) {
throw new \InvalidArgumentException('Bad function name');
}
// Fetch a list of parameter names for this function.
$params = array_map(function ($param) {
// TODO: We don't yet support optional parameters.
//
// Pass-by-reference parameters will never be supported across languages.
if ($param
->isOptional() || $param
->isPassedByReference()) {
throw new \InvalidArgumentException('Optional or pass-by-reference parameters are not supported for callback functions');
}
return $param
->getName();
}, $reflection
->getParameters());
// Construct the function's SASS signature using the parameter list.
$signature = implode(', ', array_map(function ($param) {
return '$' . $param;
}, $params));
$this->functions[$name] = (object) [
'callback' => $callback,
'name' => $name,
'params' => $params,
'signature' => $signature,
];
}
}
Classes
Name | Description |
---|---|
BackendBase | Provides a base compiler backend implementation. |