pug-php/pug-symfony

View on GitHub
src/Pug/Symfony/Traits/HelpersHandler.php

Summary

Maintainability
D
2 days
Test Coverage
File `HelpersHandler.php` has 273 lines of code (exceeds 250 allowed). Consider refactoring.
<?php
 
declare(strict_types=1);
 
namespace Pug\Symfony\Traits;
 
use Closure;
use Phug\Component\ComponentExtension;
use Psr\Container\ContainerInterface;
use Pug\Assets;
use Pug\Pug;
use Pug\Symfony\CssExtension;
use Pug\Symfony\MixedLoader;
use Pug\Twig\Environment;
use RuntimeException;
use Symfony\Bridge\Twig\Extension\AssetExtension;
use Symfony\Bridge\Twig\Extension\HttpFoundationExtension;
use Symfony\Component\Asset\Package;
use Symfony\Component\Asset\Packages;
use Symfony\Component\Asset\VersionStrategy\EmptyVersionStrategy;
use Symfony\Component\Filesystem\Filesystem;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpFoundation\UrlHelper;
use Symfony\Component\Routing\RequestContext;
use Twig\Environment as TwigEnvironment;
use Twig\Extension\ExtensionInterface;
use Twig\Loader\FilesystemLoader;
use Twig\TwigFunction;
 
/**
* Trait HelpersHandler.
*/
trait HelpersHandler
{
use PrivatePropertyAccessor;
 
`syntax error, unexpected 'ContainerInterface' (T_STRING), expecting function (T_FUNCTION) or const (T_CONST)`
protected ContainerInterface $container;
 
protected Environment $twig;
 
protected ?Pug $pug = null;
 
protected array $userOptions = [];
 
protected array $twigHelpers;
 
/**
* @var callable
*/
protected $nodeHandler;
 
/**
* Get the Pug engine.
*
* @return Pug
*/
public function getRenderer(): Pug
{
if ($this->pug === null) {
$this->pug = $this->createEngine($this->getRendererOptions());
$this->copyTwigFunctions();
$this->initializePugPlugins();
$this->share($this->getTwig()->getGlobals());
$this->setOption('paths', array_unique($this->getOptionDefault('paths', [])));
}
 
return $this->pug;
}
 
public function onNode(callable $nodeHandler): void
{
$this->nodeHandler = $nodeHandler;
}
 
public function getTwig(): Environment
{
return $this->twig;
}
 
Method `getRendererOptions` has 45 lines of code (exceeds 25 allowed). Consider refactoring.
Function `getRendererOptions` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.
The method getRendererOptions() has a Cyclomatic Complexity of 11. The configured cyclomatic complexity threshold is 10.
Avoid assigning values to variables in if clauses and the like (line '88', column '18').
Avoid assigning values to variables in if clauses and the like (line '89', column '26').
protected function getRendererOptions(): array
{
if ($this->options === null) {
$environment = $this->kernel->getEnvironment();
$projectDirectory = $this->kernel->getProjectDir();
$assetsDirectories = [$projectDirectory.'/Resources/assets'];
$viewDirectories = [$projectDirectory.'/templates'];
 
if (($loader = $this->getTwig()->getLoader()) instanceof FilesystemLoader &&
is_array($paths = $loader->getPaths()) &&
isset($paths[0])
) {
$viewDirectories[] = $paths[0];
}
 
$srcDir = $projectDirectory.'/src';
$assetsDirectories[] = $srcDir.'/Resources/assets';
$webDir = $projectDirectory.'/public';
$baseDir = $this->userOptions['baseDir']
?? $this->crawlDirectories($srcDir, $assetsDirectories, $viewDirectories);
$baseDir = $baseDir && file_exists($baseDir) ? realpath($baseDir) : $baseDir;
$this->defaultTemplateDirectory = $baseDir;
 
if (isset($this->userOptions['paths'])) {
$viewDirectories = array_merge($viewDirectories, $this->userOptions['paths'] ?: []);
}
 
$debug = $this->kernel->isDebug();
$options = array_merge([
'debug' => $debug,
'assetDirectory' => static::extractUniquePaths($assetsDirectories),
'viewDirectories' => static::extractUniquePaths($viewDirectories),
'baseDir' => $baseDir,
'cache' => $debug ? false : $this->getCacheDir(),
'environment' => $environment,
'extension' => ['.pug', '.jade'],
'outputDirectory' => $webDir,
'prettyprint' => $debug,
'on_node' => $this->nodeHandler,
], $this->userOptions);
$cache = $options['cache'] ?? $options['cache_dir'] ?? null;
 
if ($cache) {
(new Filesystem())->mkdir($cache);
}
 
$options['paths'] = array_unique(array_filter(
$options['viewDirectories'],
Space before opening parenthesis of function call prohibited
Line indented incorrectly; expected 12 spaces, found 16
static fn ($path) => $path !== $baseDir,
));
 
$this->options = $options;
}
 
return $this->options;
}
 
protected function createEngine(array $options): Pug
{
$pug = new Pug($options);
/** @var Closure|null $transformation */
$transformation = $pug->hasOption('patterns')
? ($pug->getOption('patterns')['transform_expression'] ?? null)
: null; // @codeCoverageIgnore
$pug->setOptionsRecursive([
'patterns' => [
'transform_expression' => function ($code) use ($transformation) {
if ($transformation) {
$code = $transformation($code);
}
 
return $this->interpolateTwigFunctions($code);
},
],
]);
 
return $pug;
}
 
protected function initializePugPlugins(): void
{
$pug = $this->getRenderer();
 
if ($this->userOptions['assets'] ?? true) {
$this->assets = new Assets($pug);
}
 
if ($this->userOptions['component'] ?? true) {
ComponentExtension::enable($pug);
 
$this->componentExtension = $pug->getModule(ComponentExtension::class);
 
$pug->getCompiler()->setOption('mixin_keyword', $pug->getOption('mixin_keyword'));
}
}
 
Function `interpolateTwigFunctions` has a Cognitive Complexity of 38 (exceeds 5 allowed). Consider refactoring.
Method `interpolateTwigFunctions` has 60 lines of code (exceeds 25 allowed). Consider refactoring.
The method interpolateTwigFunctions() has a Cyclomatic Complexity of 18. The configured cyclomatic complexity threshold is 10.
protected function interpolateTwigFunctions(string $code): string
{
$tokens = array_slice(token_get_all('<?php '.$code), 1);
$output = '';
$count = count($tokens);
 
for ($index = 0; $index < $count; $index++) {
$token = $tokens[$index];
 
if (is_array($token) && $token[0] === T_STRING && ($tokens[$index + 1] ?? null) === '(') {
if ($token[1] === 'function_exists') {
if ($tokens[$index + 3] === ')' &&
is_array($tokens[$index + 2]) &&
$tokens[$index + 2][0] === T_CONSTANT_ENCAPSED_STRING &&
isset($this->twigHelpers[substr($tokens[$index + 2][1], 1, -1)])
) {
$output .= 'true';
$index += 3;
continue;
}
} elseif (isset($this->twigHelpers[$token[1]])) {
$index += 2;
$arguments = [];
Avoid excessively long variable names like $argumentNeedInterpolation. Keep variable name length under 20.
$argumentNeedInterpolation = false;
$argument = '';
 
for ($opening = 1; $opening !== 0; $index++) {
Avoid deeply nested control flow statements.
switch ($tokens[$index]) {
case '(':
$opening++;
$argumentNeedInterpolation = true;
$argument .= '(';
 
break;
 
case ')':
if ((--$opening) !== 0) {
$argument .= ')';
 
break;
}
 
break 2;
 
case ',':
if ($opening > 1) {
$argument .= ',';
 
break;
}
 
$this->pushArgument($arguments, $argument, $argumentNeedInterpolation);
 
break;
 
default:
$argument .= $this->getTokenImage($tokens[$index]);
}
}
 
$this->pushArgument($arguments, $argument, $argumentNeedInterpolation);
$placeholders = [];
 
foreach ($arguments as $number => $argument) {
$placeholders["\"__argument_placeholder_$number\""] = $argument;
}
 
$output .= strtr($this->getTwig()->compileCode(
$this->twigHelpers[$token[1]],
'{{ '.$token[1].'('.implode(', ', array_keys($placeholders)).') | raw }}'
), $placeholders);
 
continue;
}
}
 
$output .= $this->getTokenImage($token);
}
 
return $output;
}
 
protected function getTokenImage(array|string $token): string
{
return is_array($token) ? $token[1] : $token;
}
 
Avoid excessively long variable names like $argumentNeedInterpolation. Keep variable name length under 20.
protected function pushArgument(array &$arguments, string &$argument, bool &$argumentNeedInterpolation): void
{
$argument = trim($argument);
 
if ($argument !== '') {
if ($argumentNeedInterpolation) {
$argument = $this->interpolateTwigFunctions($argument);
$argumentNeedInterpolation = false;
}
 
$arguments[] = $argument;
}
 
$argument = '';
}
 
protected function copyTwigFunction(TwigFunction $function): void
{
$name = $function->getName();
 
if (!preg_match('/^[a-zA-Z0-9_]+$/', $name)) {
// Methods like render_* not yet supported
return;
}
 
$this->twigHelpers[$name] = $function;
}
 
protected function enhanceTwig($twig): void
{
$twig ??= $this->container->has('twig') ? $this->container->get('twig') : null;
 
if (!($twig instanceof TwigEnvironment)) {
throw new RuntimeException('Twig needs to be configured.');
}
 
$this->twig = Environment::fromTwigEnvironment($twig, $this, $this->container);
}
 
Function `copyTwigFunctions` has a Cognitive Complexity of 12 (exceeds 5 allowed). Consider refactoring.
Method `copyTwigFunctions` has 33 lines of code (exceeds 25 allowed). Consider refactoring.
protected function copyTwigFunctions(): void
{
$this->twigHelpers = [];
$twig = $this->getTwig();
$twig->env = $twig;
$loader = new MixedLoader($twig->getLoader());
$twig->setLoader($loader);
$this->share('twig', $twig);
$twig->extensions = $twig->getExtensions();
 
/** @var AssetExtension $assetExtension */
$assetExtension = $twig->extensions[AssetExtension::class] ?? null;
 
if (!$assetExtension) {
$assetExtension = new AssetExtension(new Packages(new Package(new EmptyVersionStrategy())));
$twig->extensions[AssetExtension::class] = $assetExtension;
 
if (!$twig->hasExtension(AssetExtension::class)) {
$twig->addExtension($assetExtension);
}
}
 
$helpers = [
'css' => new CssExtension($assetExtension),
'http' => $this->getHttpFoundationExtension(),
];
 
foreach ($helpers as $helper) {
$class = get_class($helper);
 
if (!isset($twig->extensions[$class])) {
$twig->extensions[$class] = $helper;
 
if (!$twig->hasExtension($class)) {
$twig->addExtension($helper);
}
}
}
 
foreach ($twig->extensions as $extension) {
/* @var ExtensionInterface $extension */
foreach ($extension->getFunctions() as $function) {
$this->copyTwigFunction($function);
}
}
}
 
protected function getHttpFoundationExtension(): HttpFoundationExtension
{
/* @var RequestStack $stack */
$stack = $this->stack ?? $this->container->get('request_stack');
 
/* @var RequestContext $context */
$context = $this->context ?? (
$this->container->has('router.request_context')
? $this->container->get('router.request_context')
: $this->container->get('router')->getContext()
);
 
return new HttpFoundationExtension(new UrlHelper($stack, $context));
}
}