[AsseticBundle] initial entry of assetic integration

This commit is contained in:
Kris Wallsmith 2011-02-10 13:05:04 -08:00 committed by Fabien Potencier
parent b5972f3447
commit 1292925702
52 changed files with 1952 additions and 0 deletions

View File

@ -15,6 +15,7 @@ $loader->registerNamespaces(array(
'Doctrine\\DBAL' => __DIR__.'/vendor/doctrine-dbal/lib',
'Doctrine' => __DIR__.'/vendor/doctrine/lib',
'Zend' => __DIR__.'/vendor/zend/library',
'Assetic' => __DIR__.'/vendor/assetic/src',
));
$loader->registerPrefixes(array(
'Swift_' => __DIR__.'/vendor/swiftmailer/lib/classes',

View File

@ -0,0 +1,35 @@
<?php
/*
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Symfony\Bundle\AsseticBundle;
use Symfony\Bundle\AsseticBundle\DependencyInjection\Compiler\AssetManagerPass;
use Symfony\Bundle\AsseticBundle\DependencyInjection\Compiler\FilterManagerPass;
use Symfony\Bundle\AsseticBundle\DependencyInjection\Compiler\TemplatingPass;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\HttpKernel\Bundle\Bundle;
/**
* Assetic integration.
*
* @author Kris Wallsmith <kris.wallsmith@symfony-project.com>
*/
class AsseticBundle extends Bundle
{
public function registerExtensions(ContainerBuilder $container)
{
parent::registerExtensions($container);
$container->addCompilerPass(new AssetManagerPass());
$container->addCompilerPass(new FilterManagerPass());
$container->addCompilerPass(new TemplatingPass());
}
}

View File

@ -0,0 +1,38 @@
<?php
/*
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Symfony\Bundle\AsseticBundle\CacheWarmer;
use Assetic\AssetManager;
use Assetic\AssetWriter;
use Symfony\Component\HttpKernel\CacheWarmer\CacheWarmer;
class AssetWriterCacheWarmer extends CacheWarmer
{
protected $am;
protected $writer;
public function __construct(AssetManager $am, AssetWriter $writer)
{
$this->am = $am;
$this->writer = $writer;
}
public function warmUp($cacheDir)
{
$this->writer->writeManagerAssets($this->am);
}
public function isOptional()
{
return false;
}
}

View File

@ -0,0 +1,58 @@
<?php
/*
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Symfony\Bundle\AsseticBundle\CacheWarmer;
use Symfony\Bundle\AsseticBundle\Templating\FormulaLoader;
use Symfony\Component\Finder\Finder;
use Symfony\Component\HttpKernel\CacheWarmer\CacheWarmer;
use Symfony\Component\HttpKernel\Kernel;
class PhpTemplatingAssetsCacheWarmer extends CacheWarmer
{
protected $kernel;
protected $loader;
public function __construct(Kernel $kernel, FormulaLoader $loader)
{
$this->kernel = $kernel;
$this->loader = $loader;
}
public function warmUp($cacheDir)
{
$formulae = array();
foreach ($this->kernel->getBundles() as $name => $bundle) {
if (is_dir($dir = $bundle->getPath().'/Resources/views/')) {
$finder = new Finder();
$finder->files()->name('*.php')->in($dir);
foreach ($finder as $file) {
$formulae += $this->loader->load($name.':'.substr($file->getPath(), strlen($dir)).':'.$file->getBasename());
}
}
}
if (is_dir($dir = $this->kernel->getRootDir().'/views/')) {
$finder = new Finder();
$finder->files()->name('*.php')->in($dir);
foreach ($finder as $file) {
$formulae += $this->loader->load(':'.substr($file->getPath(), strlen($dir)).':'.$file->getBasename());
}
}
$this->writeCacheFile($cacheDir.'/assetic_php_templating_assets.php', '<?php return '.var_export($formulae, true).';');
}
public function isOptional()
{
return false;
}
}

View File

@ -0,0 +1,58 @@
<?php
/*
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Symfony\Bundle\AsseticBundle\CacheWarmer;
use Assetic\Extension\Twig\FormulaLoader;
use Symfony\Component\Finder\Finder;
use Symfony\Component\HttpKernel\CacheWarmer\CacheWarmer;
use Symfony\Component\HttpKernel\Kernel;
class TwigAssetsCacheWarmer extends CacheWarmer
{
protected $kernel;
protected $loader;
public function __construct(Kernel $kernel, FormulaLoader $loader)
{
$this->kernel = $kernel;
$this->loader = $loader;
}
public function warmUp($cacheDir)
{
$formulae = array();
foreach ($this->kernel->getBundles() as $name => $bundle) {
if (is_dir($dir = $bundle->getPath().'/Resources/views/')) {
$finder = new Finder();
$finder->files()->name('*.twig')->in($dir);
foreach ($finder as $file) {
$formulae += $this->loader->load($name.':'.substr($file->getPath(), strlen($dir)).':'.$file->getBasename());
}
}
}
if (is_dir($dir = $this->kernel->getRootDir().'/views/')) {
$finder = new Finder();
$finder->files()->name('*.twig')->in($dir);
foreach ($finder as $file) {
$formulae += $this->loader->load(':'.substr($file->getPath(), strlen($dir)).':'.$file->getBasename());
}
}
$this->writeCacheFile($cacheDir.'/assetic_twig_assets.php', '<?php return '.var_export($formulae, true).';');
}
public function isOptional()
{
return false;
}
}

View File

@ -0,0 +1,56 @@
<?php
/*
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Symfony\Bundle\AsseticBundle\Command;
use Symfony\Bundle\FrameworkBundle\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
/**
* Dumps assets to the filesystem.
*
* @author Kris Wallsmith <kris.wallsmith@symfony-project.com>
*/
class DumpCommand extends Command
{
protected function configure()
{
$this
->setName('assetic:dump')
->setDescription('Dumps all assets to the filesystem')
->addArgument('base_dir', InputArgument::OPTIONAL, 'The base directory')
;
}
protected function execute(InputInterface $input, OutputInterface $output)
{
if (!$baseDir = $input->getArgument('base_dir')) {
$baseDir = $this->container->getParameter('assetic.document_root');
}
$am = $this->container->get('assetic.asset_manager');
foreach ($am->all() as $name => $asset) {
$output->writeln('<info>[asset]</info> '.$name);
$asset->load();
$target = $baseDir . '/' . $asset->getTargetUrl();
if (!is_dir($dir = dirname($target))) {
$output->writeln('<info>[dir+]</info> '.$dir);
mkdir($dir);
}
$output->writeln('<info>[file+]</info> '.$asset->getTargetUrl());
file_put_contents($target, $asset->dump());
}
}
}

View File

@ -0,0 +1,60 @@
<?php
/*
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Symfony\Bundle\AsseticBundle\Controller;
use Assetic\AssetManager;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
/**
* Serves assets.
*
* @author Kris Wallsmith <kris.wallsmith@symfony-project.com>
*/
class Controller
{
protected $request;
protected $response;
protected $am;
public function __construct(Request $request, Response $response, AssetManager $am)
{
$this->request = $request;
$this->response = $response;
$this->am = $am;
}
public function render($name)
{
if (!$this->am->has($name)) {
throw new NotFoundHttpException('Asset Not Found');
}
$asset = $this->am->get($name);
// validate if-modified-since
if (null !== $lastModified = $asset->getLastModified()) {
$date = new \DateTime();
$date->setTimestamp($lastModified);
$this->response->setLastModified($date);
if ($this->response->isNotModified($this->request)) {
return $this->response;
}
}
$this->response->setContent($asset->dump());
return $this->response;
}
}

View File

@ -0,0 +1,131 @@
<?php
/*
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Symfony\Bundle\AsseticBundle\DependencyInjection;
use Symfony\Component\DependencyInjection\Extension\Extension;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Loader\FileLocator;
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
/**
* Semantic asset configuration.
*
* @author Kris Wallsmith <kris.wallsmith@symfony-project.com>
*/
class AsseticExtension extends Extension
{
/**
* Loads the configuration.
*
* When the debug flag is true, files in an asset collections will be
* rendered individually.
*
* In XML:
*
* <assetic:config
* debug="true"
* use-controller="true"
* document-root="/path/to/document/root"
* closure="/path/to/google_closure/compiler.jar"
* yui="/path/to/yuicompressor.jar"
* default-javascripts-output="js/build/*.js"
* default-stylesheets-output="css/build/*.css"
* />
*
* In YAML:
*
* assetic:
* debug: true
* use_controller: true
* document_root: /path/to/document/root
* closure: /path/to/google_closure/compiler.jar
* yui: /path/to/yuicompressor.jar
* default_javascripts_output: js/build/*.js
* default_stylesheets_output: css/build/*.css
*
* @param array $configs An array of configuration settings
* @param ContainerBuilder $container A ContainerBuilder instance
*/
public function configLoad($configs, ContainerBuilder $container)
{
$loader = new XmlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config'));
$loader->load('assetic.xml');
$loader->load('templating_twig.xml');
// $loader->load('templating_php.xml'); // not ready yet
foreach (self::normalizeKeys($configs) as $config) {
if (isset($config['debug'])) {
$container->setParameter('assetic.debug', $config['debug']);
}
if (isset($config['use_controller'])) {
$container->setParameter('assetic.use_controller', $config['use_controller']);
}
if (isset($config['document_root'])) {
$container->setParameter('assetic.document_root', $config['document_root']);
}
if (isset($config['closure'])) {
$container->setParameter('assetic.google_closure_compiler_jar', $config['closure']);
}
if (isset($config['yui'])) {
$container->setParameter('assetic.yui_jar', $config['yui']);
}
if (isset($config['default_javascripts_output'])) {
$container->setParameter('assetic.default_javascripts_output', $config['default_javascripts_output']);
}
if (isset($config['default_stylesheets_output'])) {
$container->setParameter('assetic.default_stylesheets_output', $config['default_stylesheets_output']);
}
}
if ($container->getParameterBag()->resolveValue($container->getParameterBag()->get('assetic.use_controller'))) {
$loader->load('controller.xml');
$container->setParameter('assetic.twig_extension.class', '%assetic.twig_extension.dynamic.class%');
} else {
$loader->load('asset_writer.xml');
$container->setParameter('assetic.twig_extension.class', '%assetic.twig_extension.static.class%');
}
if ($container->hasParameter('assetic.google_closure_compiler_jar')) {
$loader->load('google_closure_compiler.xml');
}
if ($container->hasParameter('assetic.yui_jar')) {
$loader->load('yui_compressor.xml');
}
}
/**
* Returns the base path for the XSD files.
*
* @return string The XSD base path
*/
public function getXsdValidationBasePath()
{
return __DIR__ . '/../Resources/config/schema';
}
public function getNamespace()
{
return 'http://www.symfony-project.org/schema/dic/assetic';
}
public function getAlias()
{
return 'assetic';
}
}

View File

@ -0,0 +1,40 @@
<?php
/*
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Symfony\Bundle\AsseticBundle\DependencyInjection\Compiler;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
/**
* Adds services tagged as assets to the asset manager.
*
* @author Kris Wallsmith <kris.wallsmith@symfony-project.com>
*/
class AssetManagerPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container)
{
if (!$container->hasDefinition('assetic.asset_manager')) {
return;
}
$am = $container->getDefinition('assetic.asset_manager');
foreach ($container->findTaggedServiceIds('assetic.asset') as $id => $attributes) {
foreach ($attributes as $attr) {
if (isset($attr['alias'])) {
$am->addMethodCall('set', array($attr['alias'], new Reference($id)));
}
}
}
}
}

View File

@ -0,0 +1,40 @@
<?php
/*
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Symfony\Bundle\AsseticBundle\DependencyInjection\Compiler;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
/**
* Adds services tagged as filters to the filter manager.
*
* @author Kris Wallsmith <kris.wallsmith@symfony-project.com>
*/
class FilterManagerPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container)
{
if (!$container->hasDefinition('assetic.filter_manager')) {
return;
}
$fm = $container->getDefinition('assetic.filter_manager');
foreach ($container->findTaggedServiceIds('assetic.filter') as $id => $attributes) {
foreach ($attributes as $attr) {
if (isset($attr['alias'])) {
$fm->addMethodCall('set', array($attr['alias'], new Reference($id)));
}
}
}
}
}

View File

@ -0,0 +1,44 @@
<?php
/*
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Symfony\Bundle\AsseticBundle\DependencyInjection\Compiler;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
class TemplatingPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container)
{
if (!$container->hasDefinition('assetic.asset_manager')) {
return;
}
$am = $container->getDefinition('assetic.asset_manager');
$engines = $container->getParameterBag()->resolveValue($container->getParameter('templating.engines'));
if (in_array('twig', $engines)) {
$am->addMethodCall('addCacheFile', array('%kernel.cache_dir%/assetic_twig_assets.php'));
} else {
foreach ($container->getTaggedServiceIds('assetic.templating.twig') as $id => $attr) {
$container->remove($id);
}
}
if (in_array('php', $engines)) {
// $am->addMethodCall('addCacheFile', array('%kernel.cache_dir%/assetic_php_assets.php'));
} else {
foreach ($container->getTaggedServiceIds('assetic.templating.php') as $id => $attr) {
$container->remove($id);
}
}
}
}

View File

@ -0,0 +1,42 @@
<?php
/*
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Symfony\Bundle\AsseticBundle\Factory;
use Assetic\Factory\AssetFactory as BaseAssetFactory;
use Symfony\Component\HttpKernel\Kernel;
/**
* Loads asset formulae from the filesystem.
*
* @author Kris Wallsmith <kris.wallsmith@symfony-project.com>
*/
class AssetFactory extends BaseAssetFactory
{
protected $kernel;
public function __construct(Kernel $kernel, $baseDir, $debug = false)
{
$this->kernel = $kernel;
parent::__construct($baseDir, $debug);
}
protected function parseAsset($sourceUrl)
{
// expand bundle notation
if ('@' == $sourceUrl[0] && false !== strpos($sourceUrl, '/')) {
$sourceUrl = $this->kernel->locateResource($sourceUrl);
}
return parent::parseAsset($sourceUrl);
}
}

View File

@ -0,0 +1,90 @@
<?php
/*
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Symfony\Bundle\AsseticBundle\Factory;
use Assetic\Factory\LazyAssetManager;
/**
* Loads asset formulae from the filesystem.
*
* @author Kris Wallsmith <kris.wallsmith@symfony-project.com>
*/
class CachedAssetManager extends LazyAssetManager
{
protected $cacheFiles = array();
protected $fresh = true;
/**
* Adds a cache file.
*
* Files added will be lazily loaded once needed.
*
* @param string $file A file that returns an array of formulae
*/
public function addCacheFile($file)
{
$this->cacheFiles[] = $file;
$this->fresh = false;
}
public function getFormulae()
{
if (!$this->fresh) {
$this->loadCacheFiles();
}
return $this->formulae;
}
public function get($name)
{
if (!$this->fresh) {
$this->loadCacheFiles();
}
return parent::get($name);
}
public function has($name)
{
if (!$this->fresh) {
$this->loadCacheFiles();
}
return parent::has($name);
}
public function all()
{
if (!$this->fresh) {
$this->loadCacheFiles();
}
return parent::all();
}
/**
* Loads formulae from the cache files.
*/
protected function loadCacheFiles()
{
while ($file = array_shift($this->cacheFiles)) {
if (!file_exists($file)) {
throw new \RuntimeException('The asset manager cache has not been warmed.');
}
$this->addFormulae(require $file);
}
$this->fresh = true;
}
}

View File

@ -0,0 +1,22 @@
<?xml version="1.0" ?>
<container xmlns="http://www.symfony-project.org/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.symfony-project.org/schema/dic/services http://www.symfony-project.org/schema/dic/services/services-1.0.xsd">
<parameters>
<parameter key="assetic.asset_writer_cache_warmer.class">Symfony\Bundle\AsseticBundle\CacheWarmer\AssetWriterCacheWarmer</parameter>
<parameter key="assetic.asset_writer.class">Assetic\AssetWriter</parameter>
</parameters>
<services>
<service id="assetic.asset_writer_cache_warmer" class="%assetic.asset_writer_cache_warmer.class%" public="false">
<tag name="kernel.cache_warmer" />
<argument type="service" id="assetic.asset_manager" />
<argument type="service" id="assetic.asset_writer" />
</service>
<service id="assetic.asset_writer" class="%assetic.asset_writer.class%" public="false">
<argument>%assetic.document_root%</argument>
</service>
</services>
</container>

View File

@ -0,0 +1,82 @@
<?xml version="1.0" ?>
<container xmlns="http://www.symfony-project.org/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.symfony-project.org/schema/dic/services http://www.symfony-project.org/schema/dic/services/services-1.0.xsd">
<parameters>
<parameter key="assetic.asset_factory.class">Symfony\Bundle\AsseticBundle\Factory\AssetFactory</parameter>
<parameter key="assetic.asset_manager.class">Symfony\Bundle\AsseticBundle\Factory\CachedAssetManager</parameter>
<parameter key="assetic.filter_manager.class">Assetic\FilterManager</parameter>
<parameter key="assetic.filter.coffee.class">Assetic\Filter\CoffeeScriptFilter</parameter>
<parameter key="assetic.filter.cssrewrite.class">Assetic\Filter\CssRewriteFilter</parameter>
<parameter key="assetic.filter.google_closure_compiler_api.class">Assetic\Filter\GoogleClosure\CompilerApiFilter</parameter>
<parameter key="assetic.filter.google_closure_compiler_jar.class">Assetic\Filter\GoogleClosure\CompilerJarFilter</parameter>
<parameter key="assetic.filter.less.class">Assetic\Filter\LessFilter</parameter>
<parameter key="assetic.filter.sass.class">Assetic\Filter\Sass\SassFilter</parameter>
<parameter key="assetic.filter.scss.class">Assetic\Filter\Sass\ScssFilter</parameter>
<parameter key="assetic.filter.sprockets.class">Assetic\Filter\SprocketsFilter</parameter>
<parameter key="assetic.debug">%kernel.debug%</parameter>
<parameter key="assetic.use_controller">%kernel.debug%</parameter>
<parameter key="assetic.document_root">%kernel.root_dir%/../web</parameter>
<parameter key="assetic.default_javascripts_output">js/*.js</parameter>
<parameter key="assetic.default_stylesheets_output">css/*.css</parameter>
<parameter key="assetic.java_bin">/usr/bin/java</parameter>
<parameter key="assetic.sass_bin">/usr/bin/sass</parameter>
<parameter key="assetic.lessc_bin">/usr/bin/lessc</parameter>
<parameter key="assetic.sprocketize_bin">/usr/bin/sprocketize</parameter>
<parameter key="assetic.coffee_bin">/usr/bin/coffee</parameter>
</parameters>
<services>
<!-- managers -->
<service id="assetic.filter_manager" class="%assetic.filter_manager.class%" />
<service id="assetic.asset_manager" class="%assetic.asset_manager.class%">
<argument type="service" id="assetic.asset_factory" />
</service>
<service id="assetic.asset_factory" class="%assetic.asset_factory.class%" public="false">
<argument type="service" id="kernel" />
<argument>%assetic.document_root%</argument>
<argument>%assetic.debug%</argument>
<call method="setFilterManager"><argument type="service" id="assetic.filter_manager" /></call>
</service>
<!-- filters -->
<service id="assetic.filter.cssrewrite" class="%assetic.filter.cssrewrite.class%" public="false">
<tag name="assetic.filter" alias="cssrewrite" />
<argument type="service" id="assetic.css_tokenizer" />
</service>
<service id="assetic.filter.less" class="%assetic.filter.less.class%" public="false">
<tag name="assetic.filter" alias="less" />
<argument>%assetic.lessc_bin%</argument>
</service>
<service id="assetic.filter.sass" class="%assetic.filter.sass.class%" public="false">
<tag name="assetic.filter" alias="sass" />
<argument>%assetic.sass_bin%</argument>
</service>
<service id="assetic.filter.scss" class="%assetic.filter.scss.class%" public="false">
<tag name="assetic.filter" alias="scss" />
<argument>%assetic.sass_bin%</argument>
</service>
<service id="assetic.filter.google_closure_compiler" class="%assetic.filter.google_closure_compiler_api.class%" public="false">
<tag name="assetic.filter" alias="closure" />
</service>
<service id="assetic.filter.sprockets" class="%assetic.filter.sprockets.class%" public="false">
<tag name="assetic.filter" alias="sprockets" />
<argument>%assetic.document_root%</argument>
<argument>%assetic.sprocketize_bin%</argument>
</service>
<service id="assetic.filter.coffee" class="%assetic.filter.coffee.class%" public="false">
<tag name="assetic.filter" alias="coffee" />
<argument>%assetic.coffee_bin%</argument>
</service>
<!-- other -->
<service id="assetic.css_tokenizer" class="PHP_CodeSniffer_Tokenizers_CSS" public="false">
<file>PHP/CodeSniffer.php</file>
</service>
</services>
</container>

View File

@ -0,0 +1,23 @@
<?xml version="1.0" ?>
<container xmlns="http://www.symfony-project.org/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.symfony-project.org/schema/dic/services http://www.symfony-project.org/schema/dic/services/services-1.0.xsd">
<parameters>
<parameter key="assetic.controller.class">Symfony\Bundle\AsseticBundle\Controller\Controller</parameter>
<parameter key="assetic.routing_loader.class">Symfony\Bundle\AsseticBundle\Routing\AsseticLoader</parameter>
</parameters>
<services>
<service id="assetic.routing_loader" class="%assetic.routing_loader.class%" public="false">
<tag name="routing.loader" />
<argument type="service" id="assetic.asset_manager" />
</service>
<service id="assetic.controller" class="%assetic.controller.class%" scope="prototype">
<argument type="service" id="request" />
<argument type="service" id="response" />
<argument type="service" id="assetic.asset_manager" />
</service>
</services>
</container>

View File

@ -0,0 +1,12 @@
<?xml version="1.0" ?>
<container xmlns="http://www.symfony-project.org/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.symfony-project.org/schema/dic/services http://www.symfony-project.org/schema/dic/services/services-1.0.xsd">
<service id="assetic.filter.google_closure_compiler" class="%assetic.filter.google_closure_compiler_jar.class%">
<tag name="assetic.filter" alias="closure" />
<argument>%assetic.google_closure_compiler_jar%</argument>
<argument>%assetic.java_bin%</argument>
</service>
</container>

View File

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8" ?>
<xsd:schema xmlns="http://www.symfony-project.org/schema/dic/assetic"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://www.symfony-project.org/schema/dic/assetic"
elementFormDefault="qualified">
<xsd:element name="config">
<xsd:complexType name="config">
<xsd:attribute name="debug" type="xsd:string" />
<xsd:attribute name="use-controller" type="xsd:string" />
<xsd:attribute name="document-root" type="xsd:string" />
<xsd:attribute name="closure" type="xsd:string" />
<xsd:attribute name="yui" type="xsd:string" />
<xsd:attribute name="default-javascripts-output" type="xsd:string" />
<xsd:attribute name="default-stylesheets-output" type="xsd:string" />
</xsd:complexType>
</xsd:element>
</xsd:schema>

View File

@ -0,0 +1,34 @@
<?xml version="1.0" ?>
<container xmlns="http://www.symfony-project.org/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.symfony-project.org/schema/dic/services http://www.symfony-project.org/schema/dic/services/services-1.0.xsd">
<parameters>
<parameter key="assetic.helper.class">Symfony\Bundle\AsseticBundle\Templating\AsseticHelper</parameter>
<parameter key="assetic.cache_warmer.php.class">Symfony\Bundle\AsseticBundle\CacheWarmer\PhpTemplatingAssetsCacheWarmer</parameter>
<parameter key="assetic.php_formula_loader.class">Symfony\Bundle\AsseticBundle\Templating\FormulaLoader</parameter>
</parameters>
<services>
<service id="assetic.helper" class="%assetic.helper.class%">
<tag name="templating.helper" alias="assetic" />
<tag name="assetic.templating.php" />
<argument type="service" id="assetic.asset_factory" />
<argument>%assetic.debug%</argument>
<argument>%assetic.default_javascripts_output%</argument>
<argument>%assetic.default_stylesheets_output%</argument>
</service>
<service id="assetic.cache_warmer.php" class="%assetic.cache_warmer.php.class%" public="false">
<tag name="kernel.cache_warmer" priority="10" />
<tag name="assetic.templating.php" />
<argument type="service" id="kernel" />
<argument type="service" id="assetic.php_formula_loader" />
</service>
<service id="assetic.php_formula_loader" class="%assetic.php_formula_loader.class%" public="false">
<tag name="assetic.templating.php" />
<argument type="service" id="templating.name_parser" />
<argument type="service" id="templating.loader" />
</service>
</services>
</container>

View File

@ -0,0 +1,34 @@
<?xml version="1.0" ?>
<container xmlns="http://www.symfony-project.org/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.symfony-project.org/schema/dic/services http://www.symfony-project.org/schema/dic/services/services-1.0.xsd">
<parameters>
<parameter key="assetic.twig_extension.dynamic.class">Symfony\Bundle\AsseticBundle\Twig\DynamicExtension</parameter>
<parameter key="assetic.twig_extension.static.class">Symfony\Bundle\AsseticBundle\Twig\StaticExtension</parameter>
<parameter key="assetic.twig_formula_loader.class">Assetic\Extension\Twig\FormulaLoader</parameter>
<parameter key="assetic.cache_warmer.twig.class">Symfony\Bundle\AsseticBundle\CacheWarmer\TwigAssetsCacheWarmer</parameter>
</parameters>
<services>
<service id="assetic.twig_extension" class="%assetic.twig_extension.class%" public="false">
<tag name="twig.extension" />
<tag name="assetic.templating.twig" />
<argument type="service" id="assetic.asset_factory" />
<argument>%assetic.debug%</argument>
<argument>%assetic.default_javascripts_output%</argument>
<argument>%assetic.default_stylesheets_output%</argument>
</service>
<service id="assetic.cache_warmer.twig" class="%assetic.cache_warmer.twig.class%" public="false">
<tag name="kernel.cache_warmer" priority="10" />
<tag name="assetic.templating.twig" />
<argument type="service" id="kernel" />
<argument type="service" id="assetic.twig_formula_loader" />
</service>
<service id="assetic.twig_formula_loader" class="%assetic.twig_formula_loader.class%" public="false">
<tag name="assetic.templating.twig" />
<argument type="service" id="twig" />
</service>
</services>
</container>

View File

@ -0,0 +1,25 @@
<?xml version="1.0" ?>
<container xmlns="http://www.symfony-project.org/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.symfony-project.org/schema/dic/services http://www.symfony-project.org/schema/dic/services/services-1.0.xsd">
<parameters>
<parameter key="assetic.filter.yui_css.class">Assetic\Filter\Yui\CssCompressorFilter</parameter>
<parameter key="assetic.filter.yui_js.class">Assetic\Filter\Yui\JsCompressorFilter</parameter>
<parameter key="assetic.yui_java_bin">%assetic.java_bin%</parameter>
</parameters>
<services>
<service id="assetic.filter.yui_css" class="%assetic.filter.yui_css.class%" public="false">
<tag name="assetic.filter" alias="yui_css" />
<argument>%assetic.yui_jar%</argument>
<argument>%assetic.java_bin%</argument>
</service>
<service id="assetic.filter.yui_js" class="%assetic.filter.yui_js.class%" public="false">
<tag name="assetic.filter" alias="yui_js" />
<argument>%assetic.yui_jar%</argument>
<argument>%assetic.yui_java_bin%</argument>
</service>
</services>
</container>

View File

@ -0,0 +1,68 @@
<?php
/*
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Symfony\Bundle\AsseticBundle\Routing;
use Assetic\AssetManager;
use Symfony\Component\Routing\Loader\Loader;
use Symfony\Component\Routing\Route;
use Symfony\Component\Routing\RouteCollection;
/**
* Loads routes for all assets.
*
* Assets should only be served through the routing system for ease-of-use
* during development.
*
* For example, add the following to your application's routing_dev.yml:
*
* _assetic:
* resource: .
* type: assetic
*
* In a production environment you should use the `assetic:dump` command to
* create static asset files.
*
* @author Kris Wallsmith <kris.wallsmith@symfony-project.com>
*/
class AsseticLoader extends Loader
{
protected $am;
public function __construct(AssetManager $am)
{
$this->am = $am;
}
public function load($resource, $type = null)
{
$routes = new RouteCollection();
foreach ($this->am->all() as $name => $asset) {
$defaults = array(
'_controller' => 'assetic.controller:render',
'name' => $name,
);
if ($extension = pathinfo($asset->getTargetUrl(), PATHINFO_EXTENSION)) {
$defaults['_format'] = $extension;
}
$routes->add('assetic_'.$name, new Route($asset->getTargetUrl(), $defaults));
}
return $routes;
}
public function supports($resource, $type = null)
{
return 'assetic' == $type;
}
}

View File

@ -0,0 +1,143 @@
<?php
/*
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Symfony\Bundle\AsseticBundle\Templating;
use Assetic\Factory\AssetFactory;
use Symfony\Component\Templating\Helper\Helper;
/**
* The "assetic" templating helper.
*
* @author Kris Wallsmith <kris.wallsmith@symfony-project.com>
*/
class AsseticHelper extends Helper
{
protected $factory;
protected $debug;
protected $defaultJavascriptsOutput;
protected $defaultStylesheetsOutput;
/**
* Constructor.
*
* @param AssetFactory $factory The asset factory
* @param Boolean $debug The debug mode
* @param string $defaultJavascriptsOutput The default {@link javascripts()} output string
* @param string $defaultStylesheetsOutput The default {@link stylesheets()} output string
*/
public function __construct(AssetFactory $factory, $debug = false, $defaultJavascriptsOutput = 'js/*.js', $defaultStylesheetsOutput = 'css/*.css')
{
$this->factory = $factory;
$this->debug = $debug;
$this->defaultJavascriptsOutput = $defaultJavascriptsOutput;
$this->defaultStylesheetsOutput = $defaultStylesheetsOutput;
}
/**
* Gets the URLs for the configured asset.
*
* Usage looks something like this:
*
* <?php foreach ($view['assetic']->assets('@jquery, js/src/core/*', '?yui_js') as $url): ?>
* <script src="<?php echo $url ?>" type="text/javascript"></script>
* <?php endforeach; ?>
*
* When in debug mode, the helper returns an array of one or more URLs.
* When not in debug mode it returns an array of one URL.
*
* @param array|string $inputs An array or comma-separated list of input strings
* @param array|string $filters An array or comma-separated list of filter names
* @param array $options An array of options
*
* @return array An array of URLs for the asset
*/
public function assets($inputs = array(), $filters = array(), array $options = array())
{
$explode = function($value)
{
return array_map('trim', explode(',', $value));
};
if (!is_array($inputs)) {
$inputs = $explode($inputs);
}
if (!is_array($filters)) {
$filters = $explode($filters);
}
if (!isset($options['debug'])) {
$options['debug'] = $this->debug;
}
$coll = $this->factory->createAsset($inputs, $filters, $options);
if (!$options['debug']) {
return array($coll->getTargetUrl());
}
// create a pattern for each leaf's target url
$pattern = $coll->getTargetUrl();
if (false !== $pos = strrpos($pattern, '.')) {
$pattern = substr($pattern, 0, $pos).'_*'.substr($pattern, $pos);
} else {
$pattern .= '_*';
}
$urls = array();
foreach ($coll as $leaf) {
$asset = $this->factory->createAsset($leaf->getSourceUrl(), $filters, array(
'output' => $pattern,
'name' => 'part'.(count($urls) + 1),
'debug' => $options['debug'],
));
$urls[] = $asset->getTargetUrl();
}
return $urls;
}
/**
* Returns an array of javascript urls.
*
* This convenience method wraps {@link assets()} and provides a default
* output string.
*/
public function javascripts($inputs = array(), $filters = array(), array $options = array())
{
if (!isset($options['output'])) {
$options['output'] = $this->defaultJavascriptsOutput;
}
return $this->assets($inputs, $filters, $options);
}
/**
* Returns an array of stylesheet urls.
*
* This convenience method wraps {@link assets()} and provides a default
* output string.
*/
public function stylesheets($inputs = array(), $filters = array(), array $options = array())
{
if (!isset($options['output'])) {
$options['output'] = $this->defaultStylesheetsOutput;
}
return $this->assets($inputs, $filters, $options);
}
public function getName()
{
return 'assetic';
}
}

View File

@ -0,0 +1,57 @@
<?php
/*
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Symfony\Bundle\AsseticBundle\Templating;
use Symfony\Component\Templating\Loader\LoaderInterface;
use Symfony\Component\Templating\TemplateNameParser;
/**
* Loads formulae from PHP templates.
*
* @author Kris Wallsmith <kris.wallsmith@symfony-project.com>
*/
class FormulaLoader
{
protected $parser;
protected $loader;
public function __construct(TemplateNameParser $parser, LoaderInterface $loader)
{
$this->parser = $parser;
$this->loader = $loader;
}
public function load($templateName)
{
if (!$template = $this->loader->load($this->parser->parse($templateName))) {
return array();
}
$tokens = token_get_all($template->getContent());
/**
* @todo Find and extract asset formulae from calls to the following:
*
* * $view['assetic']->assets(...)
* * $view['assetic']->javascripts(...)
* * $view['assetic']->stylesheets(...)
* * $view->get('assetic')->assets(...)
* * $view->get('assetic')->javascripts(...)
* * $view->get('assetic')->stylesheets(...)
*
* The loader will also need to be aware of debug mode and the default
* output strings associated with each method.
*/
return array();
}
}

View File

@ -0,0 +1,69 @@
<?php
/*
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Symfony\Bundle\AsseticBundle\Tests\CacheWarmer;
use Symfony\Bundle\AsseticBundle\CacheWarmer\TwigAssetsCacheWarmer;
class TwigAssetsCacheWarmerTest extends \PHPUnit_Framework_TestCase
{
protected function setUp()
{
if (!class_exists('Assetic\\AssetManager')) {
$this->markTestSkipped('Assetic is not available.');
}
$this->kernel = $this->getMockBuilder('Symfony\\Component\\HttpKernel\\Kernel')
->disableOriginalConstructor()
->setMethods(array('registerRootDir', 'registerBundles', 'registerContainerConfiguration', 'getBundles'))
->getMock();
$this->loader = $this->getMockBuilder('Assetic\\Extension\\Twig\\FormulaLoader')
->disableOriginalConstructor()
->getMock();
$this->cacheWarmer = new TwigAssetsCacheWarmer($this->kernel, $this->loader);
// cache dir
$this->cacheDir = sys_get_temp_dir();
@unlink($this->cacheDir.'/twig_assets.php');
}
protected function tearDown()
{
@unlink($this->cacheDir.'/twig_assets.php');
}
public function testCacheWarmer()
{
$bundle = $this->getMock('Symfony\\Component\\HttpKernel\\Bundle\\BundleInterface');
$this->kernel->expects($this->once())
->method('getBundles')
->will($this->returnValue(array('MyBundle' => $bundle)));
$bundle->expects($this->once())
->method('getPath')
->will($this->returnValue(strtr(__DIR__.'/bundle', '\\', '/')));
$this->loader->expects($this->at(0))
->method('load')
->with('MyBundle::grandparent.html.twig')
->will($this->returnValue(array('grandparent' => array())));
$this->loader->expects($this->at(1))
->method('load')
->with('MyBundle:Parents/Children:child.html.twig')
->will($this->returnValue(array('child' => array())));
$this->loader->expects($this->at(2))
->method('load')
->with('MyBundle:Parents:parent.html.twig')
->will($this->returnValue(array('parent' => array())));
$this->cacheWarmer->warmUp($this->cacheDir);
}
}

View File

@ -0,0 +1,41 @@
<?php
/*
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Symfony\Bundle\AsseticBundle\Tests;
use Symfony\Bundle\AsseticBundle\Factory\CachedAssetManager;
class CachedAssetManagerTest extends \PHPUnit_Framework_TestCase
{
protected function setUp()
{
if (!class_exists('Assetic\\AssetManager')) {
$this->markTestSkipped('Assetic is not available.');
}
}
public function testLoadFormulae()
{
$file = tempnam(sys_get_temp_dir(), 'assetic');
file_put_contents($file, '<?php return array(\'foo\' => array());');
$factory = $this->getMockBuilder('Assetic\\Factory\\AssetFactory')
->disableOriginalConstructor()
->getMock();
$am = new CachedAssetManager($factory);
$am->addCacheFile($file);
$this->assertTrue($am->has('foo'), '->loadFormulae() loads formulae');
unlink($file);
}
}

View File

@ -0,0 +1,154 @@
<?php
/*
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Symfony\Bundle\AsseticBundle\Tests\DependencyInjection;
use Symfony\Bundle\AsseticBundle\DependencyInjection\AsseticExtension;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Dumper\PhpDumper;
use Symfony\Component\DependencyInjection\Scope;
use Symfony\Component\HttpFoundation\Request;
class AsseticExtensionTest extends \PHPUnit_Framework_TestCase
{
private $kernel;
private $container;
protected function setUp()
{
if (!class_exists('Assetic\\AssetManager')) {
$this->markTestSkipped('Assetic is not available.');
}
$this->kernel = $this->getMockBuilder('Symfony\\Component\\HttpKernel\\Kernel')
->disableOriginalConstructor()
->getMock();
$this->container = new ContainerBuilder();
$this->container->addScope(new Scope('request'));
$this->container->register('request', 'Symfony\\Component\\HttpFoundation\\Request')->setScope('request');
$this->container->register('response', 'Symfony\\Component\\HttpFoundation\\Response')->setScope('prototype');
$this->container->register('twig', 'Twig_Environment');
$this->container->setParameter('kernel.debug', false);
$this->container->setParameter('kernel.root_dir', __DIR__);
$this->container->setParameter('kernel.bundles', array());
}
/**
* @dataProvider getDebugModes
*/
public function testDefaultConfig($debug)
{
$this->container->setParameter('kernel.debug', $debug);
$extension = new AsseticExtension();
$extension->configLoad(array(), $this->container);
$this->assertFalse($this->container->has('assetic.filter.yui_css'), '->configLoad() does not load the yui_css filter when a yui value is not provided');
$this->assertFalse($this->container->has('assetic.filter.yui_js'), '->configLoad() does not load the yui_js filter when a yui value is not provided');
// sanity check
$container = $this->getDumpedContainer();
foreach ($container->getServiceIds() as $id) {
$container->get($id);
}
}
public function getDebugModes()
{
return array(
array(true),
array(false),
);
}
public function testYuiConfig()
{
$extension = new AsseticExtension();
$extension->configLoad(array(array('yui' => '/path/to/yuicompressor.jar')), $this->container);
$this->assertTrue($this->container->has('assetic.filter.yui_css'), '->configLoad() loads the yui_css filter when a yui value is provided');
$this->assertTrue($this->container->has('assetic.filter.yui_js'), '->configLoad() loads the yui_js filter when a yui value is provided');
// sanity check
$container = $this->getDumpedContainer();
foreach ($container->getServiceIds() as $id) {
$container->get($id);
}
}
/**
* @dataProvider getDocumentRootKeys
*/
public function testDocumentRoot($key)
{
$extension = new AsseticExtension();
$extension->configLoad(array(array($key => '/path/to/web')), $this->container);
$this->assertEquals('/path/to/web', $this->container->getParameter('assetic.document_root'), '"'.$key.'" sets document root');
}
public function getDocumentRootKeys()
{
return array(
array('document_root'),
array('document-root'),
);
}
/**
* @dataProvider getUseControllerKeys
*/
public function testUseController($bool, $includes, $omits)
{
$extension = new AsseticExtension();
$extension->configLoad(array(array('use_controller' => $bool)), $this->container);
foreach ($includes as $id) {
$this->assertTrue($this->container->has($id), '"'.$id.'" is registered when use_controller is '.$bool);
}
foreach ($omits as $id) {
$this->assertFalse($this->container->has($id), '"'.$id.'" is not registered when use_controller is '.$bool);
}
// sanity check
$container = $this->getDumpedContainer();
foreach ($container->getServiceIds() as $id) {
$container->get($id);
}
}
public function getUseControllerKeys()
{
return array(
array(true, array('assetic.routing_loader', 'assetic.controller'), array('assetic.asset_writer_cache_warmer', 'assetic.asset_writer')),
array(false, array('assetic.asset_writer_cache_warmer', 'assetic.asset_writer'), array('assetic.routing_loader', 'assetic.controller')),
);
}
private function getDumpedContainer()
{
static $i = 0;
$class = 'AsseticExtensionTestContainer'.$i++;
$this->container->compile();
$dumper = new PhpDumper($this->container);
eval('?>'.$dumper->dump(array('class' => $class)));
$container = new $class();
$container->enterScope('request');
$container->set('kernel', $this->kernel);
return $container;
}
}

View File

@ -0,0 +1,117 @@
<?php
/*
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Symfony\Bundle\AsseticBundle\Tests;
use Symfony\Bundle\AsseticBundle\Tests\Kernel\TestKernel;
use Symfony\Component\DomCrawler\Crawler;
use Symfony\Component\HttpFoundation\Request;
class FunctionalTest extends \PHPUnit_Framework_TestCase
{
protected function setUp()
{
if (!class_exists('Assetic\\AssetManager')) {
$this->markTestSkipped('Assetic is not available.');
}
$cache = __DIR__.'/Kernel/cache';
if (!is_dir($cache)) {
mkdir($cache);
} else {
shell_exec('rm -rf '.escapeshellarg(__DIR__.'/Kernel/cache/*'));
}
}
protected function tearDown()
{
shell_exec('rm -rf '.escapeshellarg(__DIR__.'/Kernel/cache'));
}
/**
* @dataProvider provideDebugAndAssetCount
*/
public function testKernel($debug, $count)
{
$kernel = new TestKernel('test', $debug);
$kernel->boot();
$container = $kernel->getContainer();
$container->get('cache_warmer')->warmUp($container->getParameter('kernel.cache_dir'));
$assets = $container->get('assetic.asset_manager')->all();
$this->assertEquals($count, count($assets));
}
/**
* @dataProvider provideDebugAndAssetCount
*/
public function testRoutes($debug, $count)
{
$kernel = new TestKernel('test', $debug);
$kernel->boot();
$container = $kernel->getContainer();
$container->get('cache_warmer')->warmUp($container->getParameter('kernel.cache_dir'));
$routes = $container->get('router')->getRouteCollection()->all();
$matches = 0;
foreach (array_keys($routes) as $name) {
if (0 === strpos($name, 'assetic_')) {
++$matches;
}
}
$this->assertEquals($count, $matches);
}
public function testTwigRenderDebug()
{
$kernel = new TestKernel('test', true);
$kernel->boot();
$container = $kernel->getContainer();
$container->enterScope('request');
$container->set('request', new Request());
$container->get('cache_warmer')->warmUp($container->getParameter('kernel.cache_dir'));
$content = $container->get('templating')->render('::layout.html.twig');
$crawler = new Crawler($content);
$this->assertEquals(2, count($crawler->filter('link[href$=".css"]')));
$this->assertEquals(2, count($crawler->filter('script[src$=".js"]')));
}
public function testPhpRenderDebug()
{
$this->markTestIncomplete('PHP templating is not ready yet.');
$kernel = new TestKernel('test', true);
$kernel->boot();
$container = $kernel->getContainer();
$container->enterScope('request');
$container->set('request', new Request());
$container->get('cache_warmer')->warmUp($container->getParameter('kernel.cache_dir'));
$content = $container->get('templating')->render('::layout.html.php');
$crawler = new Crawler($content);
$this->assertEquals(2, count($crawler->filter('link[href$=".css"]')));
$this->assertEquals(2, count($crawler->filter('script[src$=".js"]')));
}
public function provideDebugAndAssetCount()
{
return array(
array(true, 4),
array(false, 2),
);
}
}

View File

@ -0,0 +1,37 @@
<?php
/*
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Symfony\Bundle\AsseticBundle\Tests\Kernel;
use Symfony\Component\DependencyInjection\Loader\LoaderInterface;
use Symfony\Component\HttpKernel\Kernel;
class TestKernel extends Kernel
{
public function registerRootDir()
{
return __DIR__;
}
public function registerBundles()
{
return array(
new \Symfony\Bundle\FrameworkBundle\FrameworkBundle(),
new \Symfony\Bundle\TwigBundle\TwigBundle(),
new \Symfony\Bundle\AsseticBundle\AsseticBundle(),
);
}
public function registerContainerConfiguration(LoaderInterface $loader)
{
$loader->load(__DIR__.'/config/config.yml');
}
}

View File

@ -0,0 +1,21 @@
app.config:
charset: UTF-8
error_handler: null
csrf_protection:
enabled: true
secret: xxxxxxxxxx
router: { resource: "%kernel.root_dir%/config/routing.yml" }
validation: { enabled: true, annotations: true }
templating: { engines: ['twig', 'php'] }
session:
default_locale: en
lifetime: 3600
auto_start: false
twig.config:
debug: %kernel.debug%
strict_variables: %kernel.debug%
assetic.config:
use_controller: true
document_root: "%kernel.root_dir%/web"

View File

@ -0,0 +1,3 @@
_assetic:
resource: .
type: assetic

View File

@ -0,0 +1,11 @@
<!DOCTYPE html>
<html>
<head>
<title><?php $view['slots']->output('title') ?></title>
<?php $view['slots']->output('stylesheets') ?>
</head>
<body>
<?php $view['slots']->output('_content') ?>
<?php $view['slots']->output('javascripts') ?>
</body>
</html>

View File

@ -0,0 +1,11 @@
<!DOCTYPE html>
<html>
<head>
<title>{% block title '' %}</title>
{% block stylesheets '' %}
</head>
<body>
{% block content '' %}
{% block javascripts '' %}
</body>
</html>

View File

@ -0,0 +1,13 @@
<?php $view->extend('::base.html.php') ?>
<?php $view['slots']->start('stylesheets') ?>
<?php foreach($view['assetic']->stylesheets('stylesheet1.css, stylesheet2.css') as $url): ?>
<link href="<?php echo $view->escape($url) ?>" type="text/css" rel="stylesheet" />
<?php endforeach; ?>
<?php $view['slots']->stop() ?>
<?php $view['slots']->start('javascripts') ?>
<?php foreach($view['assetic']->javascripts('javascript1.js, javascript2.js') as $url): ?>
<script src="<?php echo $view->escape($url) ?>"></script>
<?php endforeach; ?>
<?php $view['slots']->stop() ?>

View File

@ -0,0 +1,13 @@
{% extends '::base.html.twig' %}
{% block stylesheets %}
{% stylesheets 'stylesheet1.css' 'stylesheet2.css' %}
<link href="{{ asset_url }}" type="text/css" rel="stylesheet" />
{% endstylesheets %}
{% endblock %}
{% block javascripts %}
{% javascripts 'javascript1.js' 'javascript2.js' %}
<script src="{{ asset_url }}"></script>
{% endjavascripts %}
{% endblock %}

View File

@ -0,0 +1 @@
// javascript1.js

View File

@ -0,0 +1 @@
// javascript2.js

View File

@ -0,0 +1 @@
/* stylesheet1.css */

View File

@ -0,0 +1 @@
/* stylesheet2.css */

View File

@ -0,0 +1,47 @@
<?php
/*
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Symfony\Bundle\AsseticBundle\Tests\Templating;
use Assetic\Asset\AssetCollection;
use Assetic\Asset\StringAsset;
use Assetic\Factory\AssetFactory;
use Symfony\Bundle\AsseticBundle\Templating\AsseticHelper;
class AsseticHelperTest extends \PHPUnit_Framework_TestCase
{
protected function setUp()
{
if (!class_exists('Assetic\\AssetManager')) {
$this->markTestSkipped('Assetic is not available.');
}
}
/**
* @dataProvider getDebugAndCount
*/
public function testUrls($debug, $count, $message)
{
$helper = new AsseticHelper(new AssetFactory('/foo', $debug), $debug);
$urls = $helper->assets(array('js/jquery.js', 'js/jquery.plugin.js'));
$this->assertInternalType('array', $urls, '->assets() returns an array');
$this->assertEquals($count, count($urls), $message);
}
public function getDebugAndCount()
{
return array(
array(false, 1, '->assets() returns one url when not in debug mode'),
array(true, 2, '->assets() returns many urls when in debug mode'),
);
}
}

View File

@ -0,0 +1,32 @@
<?php
/*
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Symfony\Bundle\AsseticBundle\Twig;
use Assetic\Extension\Twig\AsseticExtension;
use Assetic\Factory\AssetFactory;
/**
* Assetic integration.
*
* @author Kris Wallsmith <kris.wallsmith@symfony-project.com>
*/
class DynamicExtension extends AsseticExtension
{
public function getTokenParsers()
{
return array(
new DynamicTokenParser($this->factory, $this->debug),
new DynamicTokenParser($this->factory, $this->debug, $this->defaultJavascriptsOutput, 'javascripts'),
new DynamicTokenParser($this->factory, $this->debug, $this->defaultStylesheetsOutput, 'stylesheets'),
);
}
}

View File

@ -0,0 +1,36 @@
<?php
/*
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Symfony\Bundle\AsseticBundle\Twig;
use Assetic\Extension\Twig\Node;
/**
* The "dynamic" node uses a controller to render assets.
*
* @author Kris Wallsmith <kris.wallsmith@symfony-project.com>
*/
class DynamicNode extends Node
{
/**
* Renders the asset URL using Symfony's path() function.
*/
protected function getAssetUrlNode(\Twig_NodeInterface $body)
{
return new \Twig_Node_Expression_Function(
new \Twig_Node_Expression_Name('path', $body->getLine()),
new \Twig_Node(array(
new \Twig_Node_Expression_Constant('assetic_'.$this->getAttribute('asset_name'), $body->getLine()),
)),
$body->getLine()
);
}
}

View File

@ -0,0 +1,27 @@
<?php
/*
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Symfony\Bundle\AsseticBundle\Twig;
use Assetic\Extension\Twig\TokenParser;
/**
* Parses the {% assets %} tag.
*
* @author Kris Wallsmith <kris.wallsmith@symfony-project.com>
*/
class DynamicTokenParser extends TokenParser
{
static protected function createNode(\Twig_NodeInterface $body, array $sourceUrls, $targetUrl, array $filterNames, $assetName, $debug = false, $lineno = 0, $tag = null)
{
return new DynamicNode($body, $sourceUrls, $targetUrl, $filterNames, $assetName, $debug, $lineno, $tag);
}
}

View File

@ -0,0 +1,32 @@
<?php
/*
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Symfony\Bundle\AsseticBundle\Twig;
use Assetic\Extension\Twig\AsseticExtension;
use Assetic\Factory\AssetFactory;
/**
* Assetic integration.
*
* @author Kris Wallsmith <kris.wallsmith@symfony-project.com>
*/
class StaticExtension extends AsseticExtension
{
public function getTokenParsers()
{
return array(
new StaticTokenParser($this->factory, $this->debug),
new StaticTokenParser($this->factory, $this->debug, $this->defaultJavascriptsOutput, 'javascripts'),
new StaticTokenParser($this->factory, $this->debug, $this->defaultStylesheetsOutput, 'stylesheets'),
);
}
}

View File

@ -0,0 +1,36 @@
<?php
/*
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Symfony\Bundle\AsseticBundle\Twig;
use Assetic\Extension\Twig\Node;
/**
* The "static" node references a file in the web directory.
*
* @author Kris Wallsmith <kris.wallsmith@symfony-project.com>
*/
class StaticNode extends Node
{
/**
* Renders the asset URL using Symfony's asset() function.
*/
protected function getAssetUrlNode(\Twig_NodeInterface $body)
{
return new \Twig_Node_Expression_Function(
new \Twig_Node_Expression_Name('asset', $body->getLine()),
new \Twig_Node(array(
new \Twig_Node_Expression_Constant($this->getAttribute('target_url')),
)),
$body->getLine()
);
}
}

View File

@ -0,0 +1,27 @@
<?php
/*
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Symfony\Bundle\AsseticBundle\Twig;
use Assetic\Extension\Twig\TokenParser;
/**
* Parses the {% assets %} tag.
*
* @author Kris Wallsmith <kris.wallsmith@symfony-project.com>
*/
class StaticTokenParser extends TokenParser
{
static protected function createNode(\Twig_NodeInterface $body, array $sourceUrls, $targetUrl, array $filterNames, $assetName, $debug = false, $lineno = 0, $tag = null)
{
return new StaticNode($body, $sourceUrls, $targetUrl, $filterNames, $assetName, $debug, $lineno, $tag);
}
}

View File

@ -359,6 +359,7 @@ class FrameworkExtension extends Extension
));
}
$container->setParameter('templating.engines', $config['engines']);
$engines = array_map(function($engine) { return new Reference('templating.engine.'.$engine); }, $config['engines']);
// Use a deligation unless only a single engine was registered

View File

@ -111,6 +111,8 @@ abstract class FrameworkExtensionTest extends TestCase
$this->assertEquals('templating.engine.delegating', (string) $container->getAlias('templating'), '->registerTemplatingConfiguration() configures delegating loader if multiple engines are provided');
$this->assertEquals('templating.loader.chain', (string) $container->getAlias('templating.loader'), '->registerTemplatingConfiguration() configures loader chain if multiple loaders are provided');
$this->assertEquals(array('php', 'twig'), $container->getParameter('templating.engines'), '->registerTemplatingConfiguration() sets a templating.engines parameter');
}
public function testTranslator()

View File

@ -26,6 +26,9 @@ install_git()
fi
}
# Assetic
install_git assetic git://github.com/kriswallsmith/assetic.git
# Doctrine ORM
install_git doctrine git://github.com/doctrine/doctrine2.git