Skip to content

Commit

Permalink
Add filter on the module folder to avoid caching all files by Twig
Browse files Browse the repository at this point in the history
  • Loading branch information
Quetzacoalt91 committed Jan 19, 2019
1 parent e53dce6 commit 88428fd
Show file tree
Hide file tree
Showing 8 changed files with 383 additions and 0 deletions.
114 changes: 114 additions & 0 deletions src/PrestaShopBundle/Cache/ModuleTemplateCacheWarmer.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
<?php
/**
* 2007-2019 PrestaShop and contributors.
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/OSL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to http://www.prestashop.com for more information.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2019 PrestaShop SA and contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
* International Registered Trademark & Property of PrestaShop SA
*/

namespace PrestaShopBundle\Cache;

use Psr\Container\ContainerInterface;
use Symfony\Bundle\FrameworkBundle\CacheWarmer\TemplateFinderInterface;
use Symfony\Component\Finder\Finder;
use Symfony\Component\Templating\TemplateReference;
use Twig\Error\Error;
use Symfony\Bundle\TwigBundle\CacheWarmer\TemplateCacheCacheWarmer;

/**
* Generates the Twig cache for all paths, with a specific filter for given namespace
*/
class ModuleTemplateCacheWarmer extends TemplateCacheCacheWarmer
{
private $paths;

/**
* {@inheritdoc}
*/
public function __construct(ContainerInterface $container, TemplateFinderInterface $finder = null, $paths = array())
{
$this->paths = [];
$keyToRemove = array_search('Modules', $paths);
// If the key was found, move it in a new array
if (false !== $keyToRemove) {
$exceptionPath = $paths[$keyToRemove];
unset($paths[$keyToRemove]);
$this->paths = [$keyToRemove => $exceptionPath];
}
parent::__construct($container, $finder, $paths);
}

/**
* {@inheritdoc}
*/
public function warmUp($cacheDir)
{
// Default behavior for all folder except Modules
parent::warmUp($cacheDir);

$twig = $this->container->get('twig');
$templates = [];

foreach ($this->paths as $path => $namespace) {
$templates = array_merge($templates, $this->findTemplatesInFolder($namespace, $path));
}

foreach ($templates as $template) {
if ('twig' !== $template->get('engine')) {
continue;
}

try {
$twig->loadTemplate($template);
} catch (Error $e) {
// problem during compilation, give up
}
}
}

/**
* Find templates from *.twig files in the given directory.
*
* @param string $namespace The namespace for these templates
* @param string $dir The folder where to look for templates
*
* @return array An array of templates of type TemplateReferenceInterface
*/
private function findTemplatesInFolder($namespace, $dir)
{
if (!is_dir($dir)) {
return array();
}

$templates = array();
$finder = new Finder();

foreach ($finder->files()->followLinks()->name('*.twig')->in($dir) as $file) {
$name = $file->getRelativePathname();
$templates[] = new TemplateReference(
$namespace ? sprintf('@%s/%s', $namespace, $name) : $name,
'twig'
);
}

return $templates;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<?php
/**
* 2007-2018 PrestaShop.
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/OSL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to http://www.prestashop.com for more information.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2018 PrestaShop SA
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
* International Registered Trademark & Property of PrestaShop SA
*/

namespace PrestaShopBundle\DependencyInjection\Compiler;

use PrestaShopBundle\Cache\ModuleTemplateCacheWarmer;
use PrestaShopBundle\Twig\Locator\ModuleTemplateIterator;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;

/**
* Twig templates are allowed in the whole modules folder.
* However, the generated cache parse all the files found each folder in `twig.paths`,
* including php, css, js, md files and so on.
*
* This compiler pass updates the called class to filter the allowed extensions.
*/
class OverrideTwigServiceCompilerPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container)
{
$definition = $container->getDefinition('twig.cache_warmer');
$definition->setClass(ModuleTemplateCacheWarmer::class);

$definition = $container->getDefinition('twig.template_iterator');
$definition->setClass(ModuleTemplateIterator::class);
}
}
2 changes: 2 additions & 0 deletions src/PrestaShopBundle/PrestaShopBundle.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
use PrestaShopBundle\DependencyInjection\Compiler\DynamicRolePass;
use PrestaShopBundle\DependencyInjection\Compiler\LoadServicesFromModulesPass;
use PrestaShopBundle\DependencyInjection\Compiler\OverrideTranslatorServiceCompilerPass;
use PrestaShopBundle\DependencyInjection\Compiler\OverrideTwigServiceCompilerPass;
use PrestaShopBundle\DependencyInjection\Compiler\PopulateTranslationProvidersPass;
use PrestaShopBundle\DependencyInjection\Compiler\RemoveXmlCompiledContainerPass;
use PrestaShopBundle\DependencyInjection\Compiler\RouterPass;
Expand Down Expand Up @@ -58,5 +59,6 @@ public function build(ContainerBuilder $container)
$container->addCompilerPass(new RemoveXmlCompiledContainerPass(), PassConfig::TYPE_AFTER_REMOVING);
$container->addCompilerPass(new RouterPass(), PassConfig::TYPE_AFTER_REMOVING);
$container->addCompilerPass(new OverrideTranslatorServiceCompilerPass());
$container->addCompilerPass(new OverrideTwigServiceCompilerPass());
}
}
109 changes: 109 additions & 0 deletions src/PrestaShopBundle/Twig/Locator/ModuleTemplateIterator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
<?php

/**
* 2007-2018 PrestaShop.
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/OSL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to http://www.prestashop.com for more information.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2018 PrestaShop SA
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
* International Registered Trademark & Property of PrestaShop SA
*/

namespace PrestaShopBundle\Twig\Locator;

use Symfony\Bundle\TwigBundle\TemplateIterator;
use Symfony\Component\Finder\Finder;
use Symfony\Component\HttpKernel\KernelInterface;

/**
* Extended class: Iterator for all templates in bundles and in the application Resources directory.
* This one applies a specific filter on the module folder, in order to take only *.twig
*/
class ModuleTemplateIterator extends TemplateIterator
{
private $kernel;
private $rootDir;
private $templates;
private $paths;
private $defaultPath;

/**
* @param KernelInterface $kernel A KernelInterface instance
* @param string $rootDir The directory where global templates can be stored
* @param array $paths Additional Twig paths to warm
* @param string $defaultPath The directory where global templates can be stored
*/
public function __construct(KernelInterface $kernel, $rootDir, array $paths = array(), $defaultPath = null)
{
$this->paths = [];
$this->kernel = $kernel;
$this->rootDir = $rootDir;
$this->defaultPath = $defaultPath;

$keyToRemove = array_search('Modules', $paths);
// If the key was found, move it in a new array
if (false !== $keyToRemove) {
$exceptionPath = $paths[$keyToRemove];
unset($paths[$keyToRemove]);
$this->paths = [$keyToRemove => $exceptionPath];
}
parent::__construct($kernel, $rootDir, $paths, $defaultPath);
}

/**
* {@inheritdoc}
*/
public function getIterator()
{
if (null !== $this->templates) {
return $this->templates;
}

// Not done yet, we need the array back
$this->templates = iterator_to_array(parent::getIterator());

foreach ($this->paths as $dir => $namespace) {
$this->templates = array_merge($this->templates, $this->findTemplatesInDirectory($dir, $namespace));
}

return $this->templates = new \ArrayIterator(array_unique($this->templates));
}

/**
* Find templates in the given directory.
*
* @param string $dir The directory where to look for templates
* @param string|null $namespace The template namespace
*
* @return array
*/
private function findTemplatesInDirectory($dir, $namespace = null, array $excludeDirs = array())
{
if (!is_dir($dir)) {
return array();
}

$templates = array();
foreach (Finder::create()->files()->name('*.twig')->followLinks()->in($dir)->exclude($excludeDirs) as $file) {
$templates[] = (null !== $namespace ? '@' . $namespace . '/' : '') . str_replace('\\', '/', $file->getRelativePathname());
}

return $templates;
}
}
58 changes: 58 additions & 0 deletions tests/Unit/Core/Cache/ModuleTemplateCacheWarmerTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
<?php
/**
* 2007-2019 PrestaShop.
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/OSL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to http://www.prestashop.com for more information.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2019 PrestaShop SA
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
* International Registered Trademark & Property of PrestaShop SA
*/

namespace Tests\Unit\Core\Cache;

use PHPUnit\Framework\TestCase;
use PrestaShopBundle\Cache\ModuleTemplateCacheWarmer;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Twig\Environment;

// REVIEW ME: Is this class well located?
class ModuleTemplateCacheWarmerTest extends TestCase
{
public function testOnlyGenerateTwig()
{
$twigMock = $this
->getMockBuilder(Environment::class)
->disableOriginalConstructor()
->setMethods(array('loadTemplate'))
->getMock();

$container = $this->getMockBuilder(ContainerInterface::class)->disableOriginalConstructor()->getMock();
$container->expects($this->any())->method('get')->with('twig')->will($this->returnValue($twigMock));

$cacheWarmer = new ModuleTemplateCacheWarmer($container, null, [__DIR__ . '/../../Twig/Fixtures/modules' => 'Modules']);

// Actual test: Should only have one file to load
$twigMock
->expects($this->once())
->method('loadTemplate');

$cacheWarmer->warmUp(sys_get_temp_dir());
}
}
Empty file.
Empty file.
Loading

0 comments on commit 88428fd

Please sign in to comment.