src/Phug/Phug/Phug/Phug.php
<?php
namespace Phug;
use InvalidArgumentException;
use Phug\Partial\ExtensionsTrait;
use Phug\Partial\FacadeOptionsTrait;
class Phug
{
use ExtensionsTrait;
use FacadeOptionsTrait;
/**
* @internal
*
* Phug constructor.
*
* @codeCoverageIgnore
*/
private function __construct()
{
}
/**
* List of global filters stored as array where keys are filter names, and values the action callback.
*
* @var array
*/
private static $filters = [];
/**
* List of global custom keywords stored as array where keys are keyword names, and values the action callback.
*
* @var array
*/
private static $keywords = [];
/**
* The current rendering instance used for ::compile(File,Directory), ::render(File,Directory), ::display(File).
*
* @var Renderer
*/
private static $renderer = null;
/**
* Rendering class to be used to create the renderer engine.
*
* @var Renderer
*/
private static $rendererClassName = Renderer::class;
private static function getOptions(array $options = [])
{
$extras = static::getFacadeOptions();
foreach (['filters', 'keywords'] as $option) {
$method = 'get'.ucfirst($option);
$extras[$option] = static::$method();
}
return array_merge_recursive(static::getExtensionsOptions(self::$extensions, $extras), $options);
}
/**
* Set the engine class used to render templates.
*
* @example Phug::setRendererClassName(\Tale\Pug\Renderer::class)
* @example Phug::setRendererClassName(\Pug\Pug::class)
*
* @param string $rendererClassName class name of the custom renderer engine
*/
public static function setRendererClassName($rendererClassName)
{
self::$rendererClassName = $rendererClassName;
}
/**
* Get the current engine class used to render templates.
*
* @return string $rendererClassName class name of the custom renderer engine
*/
public static function getRendererClassName()
{
return self::$rendererClassName;
}
/**
* Cleanup previously set options.
*
* @example Phug::removeOptions(['filters'], ['coffee' => null]])
*
* @param array $path option base path
* @param mixed $options options to remove
*/
public static function removeOptions($path, $options)
{
if (self::$renderer && (empty($path) || self::$renderer->hasOption($path))) {
if (is_array($options)) {
foreach ($options as $key => $value) {
if (is_int($key)) {
$callbacks = self::$renderer->getOption($path);
if (!is_array($callbacks) || is_callable($callbacks)) {
$callbacks = [$callbacks];
}
self::$renderer->setOption($path, array_filter(
$callbacks,
function ($item) use ($value) {
return $item !== $value;
}
));
continue;
}
static::removeOptions(array_merge($path, [$key]), $value);
}
return;
}
self::$renderer->unsetOption($path);
}
}
/**
* Reset all static options, filters and extensions.
*/
public static function reset()
{
static::resetFacadeOptions();
self::$renderer = null;
self::$extensions = [];
self::$filters = [];
self::$keywords = [];
}
/**
* Get a renderer with global options and argument options merged.
*
* @example Phug::getRenderer([])
*
* @param array $options
*
* @return Renderer
*/
public static function getRenderer(array $options = [])
{
$options = static::getOptions($options);
if (!self::$renderer) {
$rendererClassName = self::getRendererClassName();
self::$renderer = new $rendererClassName($options);
} elseif (!empty($options)) {
self::$renderer->reInitOptions($options);
self::$renderer->getCompiler()->getFormatter()->initFormats();
}
return self::$renderer;
}
/**
* Return true if the facade already created a Renderer instance.
*
* @return bool
*/
public static function isRendererInitialized()
{
return self::$renderer !== null;
}
/**
* Return a rendered Pug template string.
*
* @param string $input pug source
* @param array $parameters variables values
* @param array $options custom options
*
* @return string
*/
public static function render($input, array $parameters = [], array $options = [])
{
return static::getRenderer($options)->render($input, $parameters);
}
/**
* Return a rendered Pug file.
*
* @param string $path path to template
* @param array $parameters variables values
* @param array $options custom options
*
* @return string
*/
public static function renderFile($path, array $parameters = [], array $options = [])
{
return static::getRenderer($options)->renderFile($path, $parameters);
}
/**
* Display a rendered Pug template string. By default, it means HTML output to the buffer.
*
* @param string $input pug source
* @param array $parameters variables values
* @param array $options custom options
*/
public static function display($input, array $parameters = [], array $options = [])
{
return static::getRenderer($options)->display($input, $parameters);
}
/**
* Display a rendered Pug file. By default, it means HTML output to the buffer.
*
* @param string $path path to template
* @param array $parameters variables values
* @param array $options custom options
*/
public static function displayFile($path, array $parameters = [], array $options = [])
{
return static::getRenderer($options)->displayFile($path, $parameters);
}
/**
* Check if a filter is available globally.
*
* @param string $name
*
* @return bool
*/
public static function hasFilter($name)
{
return isset(self::$filters[self::normalizeFilterName($name)]);
}
/**
* Get a global filter by name.
*
* @param string $name
*
* @return callable
*/
public static function getFilter($name)
{
return self::$filters[self::normalizeFilterName($name)];
}
/**
* Set a filter to the Phug facade (will be available in the current renderer instance and next static calls).
* Throws an exception if the filter is not callable and do not have a parse method.
*
* @param string $name
* @param callable|string $filter
*
* @throws PhugException
*/
public static function setFilter($name, $filter)
{
if (!(
is_callable($filter) ||
class_exists($filter) ||
method_exists($filter, 'parse')
)) {
throw new PhugException(
'Invalid '.$name.' filter given: '.
'it must be a callable or a class name.'
);
}
self::$filters[self::normalizeFilterName($name)] = $filter;
if (self::$renderer) {
self::$renderer->setOptionsRecursive(static::getOptions());
}
}
/**
* Add a filter. Throws an exception if the name is already taken.
*
* @param string $name
* @param callable|string $filter
*
* @throws PhugException
*/
public static function addFilter($name, $filter)
{
$key = self::normalizeFilterName($name);
if (isset(self::$filters[$key])) {
throw new PhugException(
'Filter '.$name.' is already set.'
);
}
self::setFilter($name, $filter);
}
/**
* Replace a filter. Throws an exception if the name is set.
*
* @param string $name
* @param callable|string $filter
*
* @throws PhugException
*/
public static function replaceFilter($name, $filter)
{
$key = self::normalizeFilterName($name);
if (!isset(self::$filters[$key])) {
throw new PhugException(
'Filter '.$name.' is not set.'
);
}
self::setFilter($name, $filter);
}
/**
* Remove a filter from the Phug facade (remove from current renderer instance).
*
* @param string $name
*/
public static function removeFilter($name)
{
$key = self::normalizeFilterName($name);
if (isset(self::$filters[$key])) {
unset(self::$filters[$key]);
if (self::$renderer) {
self::$renderer->unsetOption(['filters', $key]);
}
}
}
/**
* Get filters list added through the Phug facade.
*
* @return array
*/
public static function getFilters()
{
return self::$filters;
}
/**
* Check if a keyword is available globally.
*
* @param string $name
*
* @return bool
*/
public static function hasKeyword($name)
{
return isset(self::$keywords[self::normalizeKeywordName($name)]);
}
/**
* Get a global custom keyword by name.
*
* @param string $name
*
* @return callable
*/
public static function getKeyword($name)
{
return self::$keywords[self::normalizeKeywordName($name)];
}
/**
* Set a keyword to the Phug facade (will be available in the current renderer instance and next static calls).
* Throws an exception if the keyword is not callable.
*
* @param string $name
* @param callable|string $keyword
*
* @throws PhugException
*/
public static function setKeyword($name, $keyword)
{
if (!is_callable($keyword)) {
throw new PhugException(
'Invalid '.$name.' keyword given: '.
'it must be a callable or a class name.'
);
}
self::$keywords[self::normalizeKeywordName($name)] = $keyword;
if (self::$renderer) {
self::$renderer->setOptionsRecursive(static::getOptions());
}
}
/**
* Add a keyword. Throws an exception if the name is already taken.
*
* @param string $name
* @param callable|string $keyword
*
* @throws PhugException
*/
public static function addKeyword($name, $keyword)
{
$key = self::normalizeKeywordName($name);
if (isset(self::$keywords[$key])) {
throw new PhugException(
'Keyword '.$name.' is already set.'
);
}
self::setKeyword($name, $keyword);
}
/**
* Replace a keyword. Throws an exception if the name is set.
*
* @param string $name
* @param callable|string $keyword
*
* @throws PhugException
*/
public static function replaceKeyword($name, $keyword)
{
$key = self::normalizeKeywordName($name);
if (!isset(self::$keywords[$key])) {
throw new PhugException(
'Keyword '.$name.' is not set.'
);
}
self::setKeyword($name, $keyword);
}
/**
* Remove a keyword from the Phug facade (remove from current renderer instance).
*
* @param string $name
*/
public static function removeKeyword($name)
{
$key = self::normalizeKeywordName($name);
if (isset(self::$keywords[$key])) {
unset(self::$keywords[$key]);
if (self::$renderer) {
self::$renderer->unsetOption(['keywords', $key]);
}
}
}
/**
* Get keywords list added through the Phug facade.
*
* @return array
*/
public static function getKeywords()
{
return self::$keywords;
}
/**
* Cache a whole directory and return an array with [$successCount, $errorCount, $errorDetails].
*
* @param string|array $source input directories containing pug files
* @param string|null $destination output for compiled PHP files
* (if array given, will be used as options array)
* @param array|null $options optional options
*
* @throws RendererException
*
* @return array
*/
public static function cacheDirectory($source, $destination = null, $options = null)
{
$options = $options ?: [];
$destination = $destination ?: [];
if (!is_array($destination)) {
$destination = [
'cache_dir' => $destination,
];
}
if (!is_array($options)) {
throw new InvalidArgumentException(
'Expected $options to be an array, got: '.
(@var_export($options, true) ?: (is_object($options) ? get_class($options) : gettype($options)))
);
}
return static::getRenderer(array_merge($options, $destination))
->cacheDirectory($source);
}
/**
* Same method as cacheDirectory but with textual human-friendly output.
*
* @param string $source input directories containing pug files
* @param string|null $destination output for compiled PHP files
* (if array given, will be used as options array)
* @param array|null $options optional options
*
* @throws RendererException
*
* @return string
*/
public static function textualCacheDirectory($source, $destination = null, $options = null)
{
list($success, $errors, $errorDetails) = static::cacheDirectory($source, $destination, $options);
return
"$success templates cached.\n".
"$errors templates failed to be cached.\n".
implode('', array_map(function ($detail) {
return $detail['inputFile']."\n".
$detail['error']->getMessage()."\n".
$detail['error']->getTraceAsString();
}, $errorDetails));
}
/**
* All dynamic methods from the renderer can be called statically with Phug facade.
*
* @param $name
* @param $arguments
*
* @return mixed
*/
public static function __callStatic($name, $arguments)
{
if (!self::$renderer && static::isOptionMethod($name)) {
return static::callOption($name, $arguments);
}
return call_user_func_array([static::getRenderer(), $name], $arguments);
}
}