diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 0c037e6..98ceb8c 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -89,3 +89,6 @@ jobs: - name: Run unit test suite run: composer test + + - name: Run PHPStan analysis + run: composer run-script phpstan diff --git a/composer.json b/composer.json index dc2c6c8..889158c 100644 --- a/composer.json +++ b/composer.json @@ -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": { @@ -52,7 +55,8 @@ "scripts": { "fix-cs": "php-cs-fixer fix --config=.php-cs-fixer.php -v --show-progress=dots", "check-cs": "@fix-cs --dry-run", - "test": "phpunit" + "test": "phpunit", + "phpstan": "phpstan analyse" }, "extra": { "branch-alias": { diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon new file mode 100644 index 0000000..635a1df --- /dev/null +++ b/phpstan-baseline.neon @@ -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\\>, 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 diff --git a/phpstan.neon b/phpstan.neon new file mode 100644 index 0000000..cf9e462 --- /dev/null +++ b/phpstan.neon @@ -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 diff --git a/src/bundle/DataCollector/TwigDataCollector.php b/src/bundle/DataCollector/TwigDataCollector.php index c842b1f..848de31 100644 --- a/src/bundle/DataCollector/TwigDataCollector.php +++ b/src/bundle/DataCollector/TwigDataCollector.php @@ -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 + */ + #[\Override] public function getTemplates(): array { $registry = $this->getTemplatePathRegistry(); diff --git a/src/bundle/DependencyInjection/Compiler/AssetPathResolutionPass.php b/src/bundle/DependencyInjection/Compiler/AssetPathResolutionPass.php index f719cd8..41906ce 100644 --- a/src/bundle/DependencyInjection/Compiler/AssetPathResolutionPass.php +++ b/src/bundle/DependencyInjection/Compiler/AssetPathResolutionPass.php @@ -42,6 +42,11 @@ public function process(ContainerBuilder $container): void $container->setAlias('ibexadesign.asset_path_resolver', new Alias(ProvisionedPathResolver::class)); } + /** + * @param array> $designPathMap + * + * @return array> + */ private function preResolveAssetsPaths(AssetPathProvisionerInterface $provisioner, array $designPathMap): array { $resolvedPathsByDesign = []; diff --git a/src/bundle/DependencyInjection/Compiler/AssetThemePass.php b/src/bundle/DependencyInjection/Compiler/AssetThemePass.php index 858692e..03e0ba8 100644 --- a/src/bundle/DependencyInjection/Compiler/AssetThemePass.php +++ b/src/bundle/DependencyInjection/Compiler/AssetThemePass.php @@ -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; @@ -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(); @@ -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) { @@ -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( diff --git a/src/bundle/DependencyInjection/Compiler/TwigThemePass.php b/src/bundle/DependencyInjection/Compiler/TwigThemePass.php index 159bf59..1d22f84 100644 --- a/src/bundle/DependencyInjection/Compiler/TwigThemePass.php +++ b/src/bundle/DependencyInjection/Compiler/TwigThemePass.php @@ -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; @@ -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) { @@ -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) { @@ -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( diff --git a/src/bundle/DependencyInjection/DesignConfigParser.php b/src/bundle/DependencyInjection/DesignConfigParser.php index c1d83eb..69f637b 100644 --- a/src/bundle/DependencyInjection/DesignConfigParser.php +++ b/src/bundle/DependencyInjection/DesignConfigParser.php @@ -13,6 +13,10 @@ class DesignConfigParser implements ParserInterface { + /** + * @param array $scopeSettings + * @param string $currentScope + */ public function mapConfig(array &$scopeSettings, $currentScope, ContextualizerInterface $contextualizer): void { if (isset($scopeSettings['design'])) { @@ -20,11 +24,17 @@ public function mapConfig(array &$scopeSettings, $currentScope, ContextualizerIn } } + /** + * @param array $config + */ public function preMap(array $config, ContextualizerInterface $contextualizer): void { // Nothing to map } + /** + * @param array $config + */ public function postMap(array $config, ContextualizerInterface $contextualizer): void { // Nothing to map diff --git a/src/bundle/DependencyInjection/IbexaDesignEngineExtension.php b/src/bundle/DependencyInjection/IbexaDesignEngineExtension.php index 7992f3e..49a013a 100644 --- a/src/bundle/DependencyInjection/IbexaDesignEngineExtension.php +++ b/src/bundle/DependencyInjection/IbexaDesignEngineExtension.php @@ -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; @@ -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 $config + */ + #[\Override] public function getConfiguration(array $config, ContainerBuilder $container): ?ConfigurationInterface { return new Configuration(); @@ -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 $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' => []]; diff --git a/src/bundle/IbexaDesignEngineBundle.php b/src/bundle/IbexaDesignEngineBundle.php index 065c3bb..850b8ba 100644 --- a/src/bundle/IbexaDesignEngineBundle.php +++ b/src/bundle/IbexaDesignEngineBundle.php @@ -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; } } diff --git a/src/lib/Asset/AssetPathProvisionerInterface.php b/src/lib/Asset/AssetPathProvisionerInterface.php index 589e380..13bfe8b 100644 --- a/src/lib/Asset/AssetPathProvisionerInterface.php +++ b/src/lib/Asset/AssetPathProvisionerInterface.php @@ -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 $assetsPaths * * @return array */ diff --git a/src/lib/Asset/AssetPathResolver.php b/src/lib/Asset/AssetPathResolver.php index 4df890c..1abebb0 100644 --- a/src/lib/Asset/AssetPathResolver.php +++ b/src/lib/Asset/AssetPathResolver.php @@ -12,18 +12,14 @@ class AssetPathResolver implements AssetPathResolverInterface { - /** @var array */ - 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> $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 diff --git a/src/lib/Asset/ProvisionedPathResolver.php b/src/lib/Asset/ProvisionedPathResolver.php index 900df2e..8f28b60 100644 --- a/src/lib/Asset/ProvisionedPathResolver.php +++ b/src/lib/Asset/ProvisionedPathResolver.php @@ -9,29 +9,21 @@ use Symfony\Component\Finder\Finder; -class ProvisionedPathResolver implements AssetPathResolverInterface, AssetPathProvisionerInterface +readonly class ProvisionedPathResolver implements AssetPathResolverInterface, AssetPathProvisionerInterface { /** - * @var array> + * @param array> $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 { diff --git a/src/lib/Asset/ThemePackage.php b/src/lib/Asset/ThemePackage.php index c7e82b2..178f6fb 100644 --- a/src/lib/Asset/ThemePackage.php +++ b/src/lib/Asset/ThemePackage.php @@ -15,23 +15,25 @@ class ThemePackage implements PackageInterface, DesignAwareInterface { use DesignAwareTrait; - private AssetPathResolverInterface $pathResolver; - - private PackageInterface $innerPackage; - - public function __construct(AssetPathResolverInterface $pathResolver, PackageInterface $innerPackage) - { - $this->pathResolver = $pathResolver; - $this->innerPackage = $innerPackage; + public function __construct( + private AssetPathResolverInterface $pathResolver, + private PackageInterface $innerPackage + ) { } public function getUrl(string $path): string { - return $this->innerPackage->getUrl($this->pathResolver->resolveAssetPath($path, $this->getCurrentDesign())); + $currentDesign = $this->getCurrentDesign(); + assert(is_string($currentDesign)); + + return $this->innerPackage->getUrl($this->pathResolver->resolveAssetPath($path, $currentDesign)); } public function getVersion(string $path): string { - return $this->innerPackage->getVersion($this->pathResolver->resolveAssetPath($path, $this->getCurrentDesign())); + $currentDesign = $this->getCurrentDesign(); + assert(is_string($currentDesign)); + + return $this->innerPackage->getVersion($this->pathResolver->resolveAssetPath($path, $currentDesign)); } } diff --git a/src/lib/Templating/TemplatePathRegistry.php b/src/lib/Templating/TemplatePathRegistry.php index 93e4216..b62f86e 100644 --- a/src/lib/Templating/TemplatePathRegistry.php +++ b/src/lib/Templating/TemplatePathRegistry.php @@ -14,11 +14,9 @@ class TemplatePathRegistry implements TemplatePathRegistryInterface, Serializabl /** @var array */ private array $pathMap = []; - private string $kernelRootDir; - - public function __construct(string $kernelRootDir) - { - $this->kernelRootDir = $kernelRootDir; + public function __construct( + private string $kernelRootDir + ) { } public function mapTemplatePath(string $templateName, string $path): void @@ -41,16 +39,22 @@ public function serialize(): ?string return serialize([$this->pathMap, $this->kernelRootDir]); } - public function unserialize($serialized): void + public function unserialize(string $serialized): void { [$this->pathMap, $this->kernelRootDir] = unserialize($serialized); } + /** + * @return array{array, string} + */ public function __serialize(): array { return [$this->pathMap, $this->kernelRootDir]; } + /** + * @param array{array, string} $data + */ public function __unserialize(array $data): void { [$this->pathMap, $this->kernelRootDir] = $data; diff --git a/src/lib/Templating/Twig/TwigThemeLoader.php b/src/lib/Templating/Twig/TwigThemeLoader.php index 9b51256..7fd14b6 100644 --- a/src/lib/Templating/Twig/TwigThemeLoader.php +++ b/src/lib/Templating/Twig/TwigThemeLoader.php @@ -17,28 +17,13 @@ * Decorates regular Twig FilesystemLoader. * It resolves generic @ibexadesign namespace to the actual current namespace. */ -class TwigThemeLoader implements LoaderInterface +readonly class TwigThemeLoader implements LoaderInterface { - /** - * @var \Ibexa\DesignEngine\Templating\TemplateNameResolverInterface - */ - private TemplateNameResolverInterface $nameResolver; - - /** - * @var \Ibexa\DesignEngine\Templating\TemplatePathRegistryInterface - */ - private TemplatePathRegistryInterface $pathRegistry; - - private FilesystemLoader $innerFilesystemLoader; - public function __construct( - TemplateNameResolverInterface $templateNameResolver, - TemplatePathRegistryInterface $templatePathRegistry, - FilesystemLoader $innerFilesystemLoader + private TemplateNameResolverInterface $nameResolver, + private TemplatePathRegistryInterface $pathRegistry, + private FilesystemLoader $innerFilesystemLoader ) { - $this->innerFilesystemLoader = $innerFilesystemLoader; - $this->nameResolver = $templateNameResolver; - $this->pathRegistry = $templatePathRegistry; } public function exists(string $name): bool @@ -64,11 +49,17 @@ public function isFresh(string $name, int $time): bool return $this->innerFilesystemLoader->isFresh($this->nameResolver->resolveTemplateName($name), $time); } + /** + * @return list + */ public function getPaths(string $namespace = FilesystemLoader::MAIN_NAMESPACE): array { return $this->innerFilesystemLoader->getPaths($namespace); } + /** + * @return list + */ public function getNamespaces(): array { return $this->innerFilesystemLoader->getNamespaces(); diff --git a/tests/lib/Asset/AssetPathResolverTest.php b/tests/lib/Asset/AssetPathResolverTest.php index 8ac953f..2388052 100644 --- a/tests/lib/Asset/AssetPathResolverTest.php +++ b/tests/lib/Asset/AssetPathResolverTest.php @@ -38,6 +38,14 @@ public function testResolveInvalidDesign(): void self::assertSame($assetPath, $resolver->resolveAssetPath($assetPath, 'foo')); } + /** + * @return list}, + * 1: list, + * 2: string, + * 3: string + * }> + */ public function resolveAssetPathProvider(): array { return [ @@ -106,6 +114,9 @@ public function resolveAssetPathProvider(): array /** * @dataProvider resolveAssetPathProvider + * + * @param array{foo: array} $designPaths + * @param list $existingPaths */ public function testResolveAssetPath(array $designPaths, array $existingPaths, string $path, string $resolvedPath): void { diff --git a/tests/lib/Asset/ProvisionedPathResolverTest.php b/tests/lib/Asset/ProvisionedPathResolverTest.php index d9b97d3..94c4cf4 100644 --- a/tests/lib/Asset/ProvisionedPathResolverTest.php +++ b/tests/lib/Asset/ProvisionedPathResolverTest.php @@ -11,6 +11,7 @@ use Ibexa\DesignEngine\Asset\AssetPathResolverInterface; use Ibexa\DesignEngine\Asset\ProvisionedPathResolver; use org\bovigo\vfs\vfsStream; +use org\bovigo\vfs\vfsStreamDirectory; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; @@ -18,10 +19,7 @@ class ProvisionedPathResolverTest extends TestCase { private AssetPathResolverInterface&MockObject $innerResolver; - /** - * @var \org\bovigo\vfs\vfsStreamDirectory - */ - private $webrootDir; + private vfsStreamDirectory $webrootDir; protected function setUp(): void { diff --git a/tests/lib/Templating/ThemeTemplateNameResolverTest.php b/tests/lib/Templating/ThemeTemplateNameResolverTest.php index 42a2ec7..f4dd494 100644 --- a/tests/lib/Templating/ThemeTemplateNameResolverTest.php +++ b/tests/lib/Templating/ThemeTemplateNameResolverTest.php @@ -23,6 +23,13 @@ protected function setUp(): void $this->configResolver = $this->createMock(ConfigResolverInterface::class); } + /** + * @return list + */ public function templateNameProvider(): array { return [ @@ -45,6 +52,13 @@ public function testResolveTemplateName(?string $currentDesign, string $template self::assertSame($expectedTemplateName, $resolver->resolveTemplateName($templateName)); } + /** + * @return list + */ public function isTemplateDesignNamespacedProvider(): array { return [