-
-
Notifications
You must be signed in to change notification settings - Fork 58
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Use the Symfony assets component for the Contao assets (see #1165).
Description ----------- I started using Symfony Encore for our application asset management and stumbled over the Symfony Asset service. I think there are a ton of useful feature for Contao. - It *knows* the path to an asset by asking for the composer package. - It automatically adds the `TL_ASSETS_URL` prefix if configured. - It automatically adds the package version to the URL, which results in clever cache busting on updates. The provided code is totally working, but there are a lot more features we can implement. Missing **TODOs**: - [ ] Use asset URLs for `TL_JAVASCRIPT` and `TL_CSS` - [ ] Add support for asset URLs (absolute) to the `Contao\Combiner` - [x] Re-implement `Controller::setStaticUrls` instead of using constants in the context service. - [x] *Maybe* add `Resources/public` of all bundles as packages. - [ ] *Maybe* add a package for the backend theme - [x] Unit Tests Commits ------- 031904d Use assets service for scripts in templates 5a0b017 Add Contao components as asset packages 51bf9fc Added asset insert tag listener 4a3ca8e Added dependency for terminal42/asset-bundle ac6f255 Use page model in asset contexts and rewrite legacy method to use them 4499530 Move asset-bundle functionality to internal compiler pass 7d84fc4 Correctly name the asset listener 69ef93d Added full unit tests coverage d6495f9 Use the global page object instead of setting page model on asset context 4bb7d55 Fix the coding style. cca8353 Fix some minor issues. 2dcb725 Add a change log entry.
- Loading branch information
Showing
34 changed files
with
1,107 additions
and
49 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,132 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
/* | ||
* This file is part of Contao. | ||
* | ||
* Copyright (c) 2005-2017 Leo Feyer | ||
* | ||
* @license LGPL-3.0+ | ||
*/ | ||
|
||
namespace Contao\CoreBundle\Asset; | ||
|
||
use Contao\Config; | ||
use Contao\CoreBundle\Framework\ContaoFrameworkInterface; | ||
use Contao\PageModel; | ||
use Symfony\Component\Asset\Context\ContextInterface; | ||
use Symfony\Component\HttpFoundation\RequestStack; | ||
|
||
class ContaoContext implements ContextInterface | ||
{ | ||
/** | ||
* @var ContaoFrameworkInterface | ||
*/ | ||
private $framework; | ||
|
||
/** | ||
* @var RequestStack | ||
*/ | ||
private $requestStack; | ||
|
||
/** | ||
* @var string | ||
*/ | ||
private $field; | ||
|
||
/** | ||
* @var bool | ||
*/ | ||
private $debug; | ||
|
||
/** | ||
* @param ContaoFrameworkInterface $framework | ||
* @param RequestStack $requestStack | ||
* @param string $field | ||
* @param bool $debug | ||
*/ | ||
public function __construct(ContaoFrameworkInterface $framework, RequestStack $requestStack, string $field, bool $debug = false) | ||
{ | ||
$this->framework = $framework; | ||
$this->requestStack = $requestStack; | ||
$this->field = $field; | ||
$this->debug = $debug; | ||
} | ||
|
||
/** | ||
* {@inheritdoc} | ||
*/ | ||
public function getBasePath() | ||
{ | ||
if ($this->debug) { | ||
return ''; | ||
} | ||
|
||
$request = $this->requestStack->getCurrentRequest(); | ||
|
||
if (null === $request || '' === ($staticUrl = $this->getFieldValue($this->getPage()))) { | ||
return ''; | ||
} | ||
|
||
$protocol = $this->isSecure() ? 'https' : 'http'; | ||
$relative = preg_replace('@https?://@', '', $staticUrl); | ||
|
||
return sprintf('%s://%s%s', $protocol, $relative, $request->getBasePath()); | ||
} | ||
|
||
/** | ||
* {@inheritdoc} | ||
*/ | ||
public function isSecure(): bool | ||
{ | ||
$page = $this->getPage(); | ||
|
||
if (null !== $page) { | ||
return (bool) $page->loadDetails()->rootUseSSL; | ||
} | ||
|
||
$request = $this->requestStack->getCurrentRequest(); | ||
|
||
if (null === $request) { | ||
return false; | ||
} | ||
|
||
return $request->isSecure(); | ||
} | ||
|
||
/** | ||
* Gets the current page model. | ||
* | ||
* @return PageModel|null | ||
*/ | ||
private function getPage(): ?PageModel | ||
{ | ||
if (isset($GLOBALS['objPage']) && $GLOBALS['objPage'] instanceof PageModel) { | ||
return $GLOBALS['objPage']; | ||
} | ||
|
||
return null; | ||
} | ||
|
||
/** | ||
* Gets field value from page model or global config. | ||
* | ||
* @param PageModel|null $page | ||
* | ||
* @return string | ||
*/ | ||
private function getFieldValue(?PageModel $page): string | ||
{ | ||
if (null !== $page) { | ||
return (string) $page->{$this->field}; | ||
} | ||
|
||
$this->framework->initialize(); | ||
|
||
/** @var Config $config */ | ||
$config = $this->framework->getAdapter(Config::class); | ||
|
||
return (string) $config->get($this->field); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
166 changes: 166 additions & 0 deletions
166
src/DependencyInjection/Compiler/AddAssetsPackagesPass.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,166 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
/* | ||
* This file is part of Contao. | ||
* | ||
* Copyright (c) 2005-2017 Leo Feyer | ||
* | ||
* @license LGPL-3.0+ | ||
*/ | ||
|
||
namespace Contao\CoreBundle\DependencyInjection\Compiler; | ||
|
||
use Symfony\Component\DependencyInjection\ChildDefinition; | ||
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; | ||
use Symfony\Component\DependencyInjection\Container; | ||
use Symfony\Component\DependencyInjection\ContainerBuilder; | ||
use Symfony\Component\DependencyInjection\Definition; | ||
use Symfony\Component\DependencyInjection\Reference; | ||
use Symfony\Component\HttpKernel\Bundle\Bundle; | ||
|
||
class AddAssetsPackagesPass implements CompilerPassInterface | ||
{ | ||
/** | ||
* {@inheritdoc} | ||
*/ | ||
public function process(ContainerBuilder $container): void | ||
{ | ||
if (!$container->hasDefinition('assets.packages')) { | ||
return; | ||
} | ||
|
||
$this->addBundles($container); | ||
$this->addComponents($container); | ||
} | ||
|
||
/** | ||
* Adds every bundle with a public folder as assets package. | ||
* | ||
* @param ContainerBuilder $container | ||
*/ | ||
private function addBundles(ContainerBuilder $container): void | ||
{ | ||
$packages = $container->getDefinition('assets.packages'); | ||
$context = new Reference('contao.assets.plugins_context'); | ||
|
||
if ($container->hasDefinition('assets._version_default')) { | ||
$version = new Reference('assets._version_default'); | ||
} else { | ||
$version = new Reference('assets.empty_version_strategy'); | ||
} | ||
|
||
/** @var Bundle[] $bundles */ | ||
$bundles = $container->get('kernel')->getBundles(); | ||
|
||
foreach ($bundles as $bundle) { | ||
if (!is_dir($originDir = $bundle->getPath().'/Resources/public')) { | ||
continue; | ||
} | ||
|
||
if ($extension = $bundle->getContainerExtension()) { | ||
$packageName = $extension->getAlias(); | ||
} else { | ||
$packageName = $this->getBundlePackageName($bundle); | ||
} | ||
|
||
$serviceId = 'assets._package_'.$packageName; | ||
$basePath = 'bundles/'.preg_replace('/bundle$/', '', strtolower($bundle->getName())); | ||
|
||
$container->setDefinition($serviceId, $this->createPackageDefinition($basePath, $version, $context)); | ||
$packages->addMethodCall('addPackage', [$packageName, new Reference($serviceId)]); | ||
} | ||
} | ||
|
||
/** | ||
* Adds the Contao components as assets packages. | ||
* | ||
* @param ContainerBuilder $container | ||
*/ | ||
private function addComponents(ContainerBuilder $container): void | ||
{ | ||
if (!$container->hasParameter('kernel.packages')) { | ||
return; | ||
} | ||
|
||
$packages = $container->getDefinition('assets.packages'); | ||
$context = new Reference('contao.assets.plugins_context'); | ||
$components = $container->getParameter('kernel.packages'); | ||
|
||
foreach ($components as $name => $version) { | ||
[$vendor, $packageName] = explode('/', $name, 2); | ||
|
||
if ('contao-components' !== $vendor) { | ||
continue; | ||
} | ||
|
||
$serviceId = 'assets._package_'.$name; | ||
$basePath = 'assets/'.$packageName; | ||
$version = $this->createPackageVersion($container, $version, $name); | ||
|
||
$container->setDefinition($serviceId, $this->createPackageDefinition($basePath, $version, $context)); | ||
$packages->addMethodCall('addPackage', [$name, new Reference($serviceId)]); | ||
} | ||
} | ||
|
||
/** | ||
* Creates an assets package definition. | ||
* | ||
* @param string $basePath | ||
* @param Reference $version | ||
* @param Reference $context | ||
* | ||
* @return Definition | ||
*/ | ||
private function createPackageDefinition(string $basePath, Reference $version, Reference $context): Definition | ||
{ | ||
$package = new ChildDefinition('assets.path_package'); | ||
|
||
$package | ||
->setPublic(false) | ||
->replaceArgument(0, $basePath) | ||
->replaceArgument(1, $version) | ||
->replaceArgument(2, $context) | ||
; | ||
|
||
return $package; | ||
} | ||
|
||
/** | ||
* Creates an asset package version strategy. | ||
* | ||
* @param ContainerBuilder $container | ||
* @param string $version | ||
* @param string $name | ||
* | ||
* @return Reference | ||
*/ | ||
private function createPackageVersion(ContainerBuilder $container, string $version, string $name): Reference | ||
{ | ||
$def = new ChildDefinition('assets.static_version_strategy'); | ||
$def->replaceArgument(0, $version); | ||
|
||
$container->setDefinition('assets._version_'.$name, $def); | ||
|
||
return new Reference('assets._version_'.$name); | ||
} | ||
|
||
/** | ||
* Returns a bundle package name emulating what a bundle extension would look like. | ||
* | ||
* @param Bundle $bundle | ||
* | ||
* @return string | ||
*/ | ||
private function getBundlePackageName(Bundle $bundle): string | ||
{ | ||
$className = $bundle->getName(); | ||
|
||
if ('Bundle' === substr($className, -6)) { | ||
$className = substr($className, 0, -6); | ||
} | ||
|
||
return Container::underscore($className); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
/* | ||
* This file is part of Contao. | ||
* | ||
* Copyright (c) 2005-2017 Leo Feyer | ||
* | ||
* @license LGPL-3.0+ | ||
*/ | ||
|
||
namespace Contao\CoreBundle\EventListener\InsertTags; | ||
|
||
use Symfony\Component\Asset\Packages; | ||
|
||
class AssetListener | ||
{ | ||
/** | ||
* @var Packages | ||
*/ | ||
private $packages; | ||
|
||
/** | ||
* @param Packages $packages | ||
*/ | ||
public function __construct(Packages $packages) | ||
{ | ||
$this->packages = $packages; | ||
} | ||
|
||
/** | ||
* Replaces the "asset" insert tag. | ||
* | ||
* @param string $tag | ||
* | ||
* @return string|false | ||
*/ | ||
public function onReplaceInsertTags(string $tag) | ||
{ | ||
$chunks = explode('::', $tag); | ||
|
||
if ('asset' === $chunks[0]) { | ||
return $this->packages->getUrl($chunks[1], $chunks[2] ?? null); | ||
} | ||
|
||
return false; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.