Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@
"ibexa/doctrine-schema": "~5.0.x-dev",
"ibexa/rector": "~5.0.x-dev",
"mikey179/vfsstream": "^1.6",
"phpstan/phpstan": "^2.0",
"phpstan/phpstan-phpunit": "^2.0",
"phpstan/phpstan-symfony": "^2.0",
"phpunit/phpunit": "^9.6"
},
"autoload": {
Expand Down
31 changes: 31 additions & 0 deletions phpstan-baseline.neon
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
parameters:
ignoreErrors:
-
message: '#^Class Ibexa\\Bundle\\DesignEngine\\DataCollector\\TwigDataCollector extends @final class Symfony\\Bridge\\Twig\\DataCollector\\TwigDataCollector\.$#'
identifier: class.extendsFinalByPhpDoc
count: 1
path: src/bundle/DataCollector/TwigDataCollector.php

-
message: '#^Parameter \#1 \$provisioner of method Ibexa\\Bundle\\DesignEngine\\DependencyInjection\\Compiler\\AssetPathResolutionPass\:\:preResolveAssetsPaths\(\) expects Ibexa\\DesignEngine\\Asset\\AssetPathProvisionerInterface, object given\.$#'
identifier: argument.type
count: 1
path: src/bundle/DependencyInjection/Compiler/AssetPathResolutionPass.php

-
message: '#^Parameter \#2 \$designPathMap of method Ibexa\\Bundle\\DesignEngine\\DependencyInjection\\Compiler\\AssetPathResolutionPass\:\:preResolveAssetsPaths\(\) expects array\<string, list\<string\>\>, array\|bool\|float\|int\|string\|null given\.$#'
identifier: argument.type
count: 1
path: src/bundle/DependencyInjection/Compiler/AssetPathResolutionPass.php

-
message: '#^Parameter \#3 \$length of function substr expects int\|null, int\|false given\.$#'
identifier: argument.type
count: 1
path: src/bundle/DependencyInjection/Compiler/AssetThemePass.php

-
message: '#^PHPDoc tag @var with type SplFileInfo is not subtype of native type Symfony\\Component\\Finder\\SplFileInfo\.$#'
identifier: varTag.nativeType
count: 1
path: src/lib/Asset/ProvisionedPathResolver.php
11 changes: 11 additions & 0 deletions phpstan.neon
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
includes:
- phpstan-baseline.neon
- vendor/phpstan/phpstan-phpunit/extension.neon
- vendor/phpstan/phpstan-symfony/extension.neon

parameters:
level: 8
paths:
- src
- tests
treatPhpDocTypesAsCertain: false
21 changes: 11 additions & 10 deletions src/bundle/DataCollector/TwigDataCollector.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,29 +14,30 @@

class TwigDataCollector extends BaseCollector
{
private TemplatePathRegistryInterface $templatePathRegistry;

public function __construct(Profile $profile, Environment $environment, TemplatePathRegistryInterface $templatePathRegistry)
{
public function __construct(
Profile $profile,
Environment $environment,
private TemplatePathRegistryInterface $templatePathRegistry
) {
parent::__construct($profile, $environment);
$this->templatePathRegistry = $templatePathRegistry;
}

private function getTemplatePathRegistry()
private function getTemplatePathRegistry(): TemplatePathRegistryInterface
{
if (!isset($this->templatePathRegistry)) {
$this->templatePathRegistry = unserialize($this->data['template_path_registry']);
}

return $this->templatePathRegistry;
}

#[\Override]
public function lateCollect(): void
{
parent::lateCollect();
$this->data['template_path_registry'] = serialize($this->templatePathRegistry);
}

/**
* @return array<string, int>
*/
#[\Override]
public function getTemplates(): array
{
$registry = $this->getTemplatePathRegistry();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,11 @@ public function process(ContainerBuilder $container): void
$container->setAlias('ibexadesign.asset_path_resolver', new Alias(ProvisionedPathResolver::class));
}

/**
* @param array<string, list<string>> $designPathMap
*
* @return array<string, array<string, string>>
*/
private function preResolveAssetsPaths(AssetPathProvisionerInterface $provisioner, array $designPathMap): array
{
$resolvedPathsByDesign = [];
Expand Down
17 changes: 11 additions & 6 deletions src/bundle/DependencyInjection/Compiler/AssetThemePass.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,17 +21,20 @@ public function process(ContainerBuilder $container): void
return;
}

$overridePaths = $container->getParameter('ibexa.design.assets.override_paths');
$themesPathMap = [
'_override' => array_merge(
['assets'],
$container->getParameter('ibexa.design.assets.override_paths')
(array)$overridePaths,
),
];
$finder = new Finder();
// Look for assets themes in bundles.
foreach ($container->getParameter('kernel.bundles') as $bundleName => $bundleClass) {
foreach ((array)$container->getParameter('kernel.bundles') as $bundleName => $bundleClass) {
$bundleReflection = new \ReflectionClass($bundleClass);
$bundleViewsDir = \dirname($bundleReflection->getFileName()) . '/Resources/public';
$fileName = $bundleReflection->getFileName();
assert(is_string($fileName));
$bundleViewsDir = \dirname($fileName) . '/Resources/public';
$themeDir = $bundleViewsDir . '/themes';
if (!is_dir($themeDir)) {
continue;
Expand All @@ -46,7 +49,9 @@ public function process(ContainerBuilder $container): void
}

// Look for assets themes at application level (web/assets/themes).
$appLevelThemeDir = $container->getParameter('webroot_dir') . '/assets/themes';
$webrootDir = $container->getParameter('webroot_dir');
assert(is_string($webrootDir));
$appLevelThemeDir = $webrootDir . '/assets/themes';
if (is_dir($appLevelThemeDir)) {
foreach ((new Finder())->directories()->in($appLevelThemeDir)->depth('== 0') as $directoryInfo) {
$theme = $directoryInfo->getBasename();
Expand All @@ -62,7 +67,7 @@ public function process(ContainerBuilder $container): void
}

$pathsByDesign = [];
foreach ($container->getParameter('ibexa.design.list') as $designName => $themeFallback) {
foreach ((array)$container->getParameter('ibexa.design.list') as $designName => $themeFallback) {
// Always add _override theme first.
array_unshift($themeFallback, '_override');
foreach ($themeFallback as $theme) {
Expand All @@ -77,7 +82,7 @@ public function process(ContainerBuilder $container): void
}
}

$themesList = $container->getParameter('ibexa.design.themes.list');
$themesList = (array)$container->getParameter('ibexa.design.themes.list');
$container->setParameter(
'ibexa.design.themes.list',
array_unique(
Expand Down
23 changes: 14 additions & 9 deletions src/bundle/DependencyInjection/Compiler/TwigThemePass.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,15 @@ public function process(ContainerBuilder $container): void
}

$themesPathMap = [
'_override' => $container->getParameter('ibexa.design.templates.override_paths'),
'_override' => (array)$container->getParameter('ibexa.design.templates.override_paths'),
];
$finder = new Finder();
// Look for themes in bundles.
foreach ($container->getParameter('kernel.bundles') as $bundleName => $bundleClass) {
foreach ((array)$container->getParameter('kernel.bundles') as $bundleName => $bundleClass) {
$bundleReflection = new ReflectionClass($bundleClass);
$bundleViewsDir = \dirname($bundleReflection->getFileName()) . '/Resources/views';
$filename = $bundleReflection->getFileName();
assert(is_string($filename));
$bundleViewsDir = \dirname($filename) . '/Resources/views';
$themeDir = $bundleViewsDir . '/themes';
if (!is_dir($themeDir)) {
continue;
Expand All @@ -53,9 +55,9 @@ public function process(ContainerBuilder $container): void

$twigLoaderDef = $container->findDefinition(TwigThemeLoader::class);
// Now look for themes at application level
$appLevelThemesDir = $container->getParameterBag()->resolveValue(
$container->getParameter('twig.default_path') . '/themes'
);
$twigDefaultPath = $container->getParameter('twig.default_path');
assert(is_string($twigDefaultPath));
$appLevelThemesDir = $container->getParameterBag()->resolveValue($twigDefaultPath . '/themes');

if (is_dir($appLevelThemesDir)) {
foreach ((new Finder())->directories()->in($appLevelThemesDir)->depth('== 0') as $directoryInfo) {
Expand All @@ -69,14 +71,17 @@ public function process(ContainerBuilder $container): void

// Now merge with already configured template theme paths
// Template theme paths defined via config will always have less priority than convention based paths
$themesPathMap = array_merge_recursive($themesPathMap, $container->getParameter('ibexa.design.templates.path_map'));
$themesPathMap = array_merge_recursive(
$themesPathMap,
(array)$container->getParameter('ibexa.design.templates.path_map'),
);

// De-duplicate the map
foreach ($themesPathMap as $theme => &$paths) {
$paths = array_unique($paths);
}

foreach ($container->getParameter('ibexa.design.list') as $designName => $themeFallback) {
foreach ((array)$container->getParameter('ibexa.design.list') as $designName => $themeFallback) {
// Always add _override theme first.
array_unshift($themeFallback, '_override');
foreach ($themeFallback as $theme) {
Expand All @@ -91,7 +96,7 @@ public function process(ContainerBuilder $container): void
}
}

$themesList = $container->getParameter('ibexa.design.themes.list');
$themesList = (array)$container->getParameter('ibexa.design.themes.list');
$container->setParameter(
'ibexa.design.themes.list',
array_unique(
Expand Down
10 changes: 10 additions & 0 deletions src/bundle/DependencyInjection/DesignConfigParser.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,28 @@

class DesignConfigParser implements ParserInterface
{
/**
* @param array<string, mixed> $scopeSettings
* @param string $currentScope
*/
public function mapConfig(array &$scopeSettings, $currentScope, ContextualizerInterface $contextualizer): void
{
if (isset($scopeSettings['design'])) {
$contextualizer->setContextualParameter('design', $currentScope, $scopeSettings['design']);
}
}

/**
* @param array<string, mixed> $config
*/
public function preMap(array $config, ContextualizerInterface $contextualizer): void
{
// Nothing to map
}

/**
* @param array<string, mixed> $config
*/
public function postMap(array $config, ContextualizerInterface $contextualizer): void
{
// Nothing to map
Expand Down
14 changes: 10 additions & 4 deletions src/bundle/DependencyInjection/IbexaDesignEngineExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@

namespace Ibexa\Bundle\DesignEngine\DependencyInjection;

use Ibexa\Bundle\Core\DependencyInjection\Configuration\SiteAccessAware\ConfigurationProcessor;
use Symfony\Component\Config\Definition\ConfigurationInterface;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\DependencyInjection\ContainerBuilder;
Expand All @@ -18,11 +17,16 @@ class IbexaDesignEngineExtension extends Extension
{
public const string EXTENSION_NAME = 'ibexa_design_engine';

#[\Override]
public function getAlias(): string
{
return self::EXTENSION_NAME;
}

/**
* @param array<string, mixed> $config
*/
#[\Override]
public function getConfiguration(array $config, ContainerBuilder $container): ?ConfigurationInterface
{
return new Configuration();
Expand All @@ -40,12 +44,14 @@ public function load(array $configs, ContainerBuilder $container): void
$configuration = $this->getConfiguration($configs, $container);
assert(null !== $configuration);
$config = $this->processConfiguration($configuration, $configs);
$processor = new ConfigurationProcessor($container, 'ezdesign');

$this->configureDesigns($config, $processor, $container);
$this->configureDesigns($config, $container);
}

private function configureDesigns(array $config, ConfigurationProcessor $processor, ContainerBuilder $container): void
/**
* @param array<string, mixed> $config
*/
private function configureDesigns(array $config, ContainerBuilder $container): void
{
// Always add "standard" design to the list (defaults to application level & override paths only)
$config['design_list'] += ['standard' => []];
Expand Down
3 changes: 2 additions & 1 deletion src/bundle/IbexaDesignEngineBundle.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,13 @@ public function build(ContainerBuilder $container): void
$container->addCompilerPass(new AssetPathResolutionPass(), PassConfig::TYPE_OPTIMIZE);
}

#[\Override]
public function getContainerExtension(): ?ExtensionInterface
{
if (!isset($this->extension)) {
$this->extension = new IbexaDesignEngineExtension();
}

return $this->extension;
return $this->extension === false ? null : $this->extension;
}
}
2 changes: 1 addition & 1 deletion src/lib/Asset/AssetPathProvisionerInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ interface AssetPathProvisionerInterface
* Returns a map with asset logical path as key and its resolved path (relative to webroot dir) as value.
* Example => ['images/foo.png' => 'asset/themes/some_theme/images/foo.png'].
*
* @param string[] $assetsPaths
* @param list<string> $assetsPaths
*
* @return array<string, string>
*/
Expand Down
20 changes: 8 additions & 12 deletions src/lib/Asset/AssetPathResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,14 @@

class AssetPathResolver implements AssetPathResolverInterface
{
/** @var array<string, string> */
private array $designPaths;

private string $webRootDir;

private ?LoggerInterface $logger;

public function __construct(array $designPaths, string $webRootDir, LoggerInterface $logger = null)
{
$this->designPaths = $designPaths;
$this->webRootDir = $webRootDir;
$this->logger = $logger;
/**
* @param array<string, array<int, string>> $designPaths
*/
public function __construct(
private readonly array $designPaths,
private readonly string $webRootDir,
private readonly ?LoggerInterface $logger = null
) {
}

public function resolveAssetPath(string $path, string $design): string
Expand Down
22 changes: 7 additions & 15 deletions src/lib/Asset/ProvisionedPathResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,29 +9,21 @@

use Symfony\Component\Finder\Finder;

class ProvisionedPathResolver implements AssetPathResolverInterface, AssetPathProvisionerInterface
readonly class ProvisionedPathResolver implements AssetPathResolverInterface, AssetPathProvisionerInterface
{
/**
* @var array<string, array<string, string>>
* @param array<string, array<string, string>> $resolvedPaths
*/
private array $resolvedPaths;

private AssetPathResolverInterface $innerResolver;

private string $webRootDir;

public function __construct(array $resolvedPaths, AssetPathResolverInterface $innerResolver, string $webRootDir)
{
$this->resolvedPaths = $resolvedPaths;
$this->innerResolver = $innerResolver;
$this->webRootDir = $webRootDir;
public function __construct(
private array $resolvedPaths,
private AssetPathResolverInterface $innerResolver,
private string $webRootDir
) {
}

/**
* Looks for $path within pre-resolved paths for provided design.
* If it cannot be found, fallbacks to original resolver.
*
* {@inheritdoc}
*/
public function resolveAssetPath(string $path, string $design): string
{
Expand Down
Loading
Loading