Explorar o código

Merge remote branch 'kriswallsmith/assetic/twig-functions'

* kriswallsmith/assetic/twig-functions:
  [AsseticBundle] added a listener to add common image request formats when use_controller is on
  [AsseticBundle] added a node visitor to wrap runtime filter functions with runtime use_controller check
  [AsseticBundle] added support for Twig functions
Fabien Potencier %!s(int64=14) %!d(string=hai) anos
pai
achega
fc2c1578ac

+ 3 - 0
src/Symfony/Bundle/AsseticBundle/DependencyInjection/AsseticExtension.php

@@ -71,6 +71,9 @@ class AsseticExtension extends Extension
             }
         }
 
+        // twig functions
+        $container->getDefinition('assetic.twig_extension')->replaceArgument(2, $config['twig']['functions']);
+
         // choose dynamic or static
         if ($parameterBag->resolveValue($parameterBag->get('assetic.use_controller'))) {
             $loader->load('controller.xml');

+ 23 - 0
src/Symfony/Bundle/AsseticBundle/DependencyInjection/Configuration.php

@@ -91,6 +91,29 @@ class Configuration implements ConfigurationInterface
                     ->end()
                 ->end()
             ->end()
+
+            // twig
+            ->children()
+                ->arrayNode('twig')
+                    ->addDefaultsIfNotSet()
+                    ->defaultValue(array())
+                    ->fixXmlConfig('function')
+                    ->children()
+                        ->arrayNode('functions')
+                            ->addDefaultsIfNotSet()
+                            ->defaultValue(array())
+                            ->useAttributeAsKey('name')
+                            ->prototype('variable')
+                                ->treatNullLike(array())
+                                ->validate()
+                                    ->ifTrue(function($v) { return !is_array($v); })
+                                    ->thenInvalid('The assetic.twig.functions config %s must be either null or an array.')
+                                ->end()
+                            ->end()
+                        ->end()
+                    ->end()
+                ->end()
+            ->end()
         ;
 
         return $builder;

+ 31 - 0
src/Symfony/Bundle/AsseticBundle/Listener/RequestListener.php

@@ -0,0 +1,31 @@
+<?php
+
+/*
+ * This file is part of the Symfony framework.
+ *
+ * (c) Fabien Potencier <fabien@symfony.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\Listener;
+
+use Symfony\Component\HttpKernel\Event\GetResponseEvent;
+
+/**
+ * Adds a few formats to each request.
+ *
+ * @author Kris Wallsmith <kris.wallsmith@symfony.com>
+ */
+class RequestListener
+{
+    public function onCoreRequest(GetResponseEvent $event)
+    {
+        $request = $event->getRequest();
+
+        $request->setFormat('png', 'image/png');
+        $request->setFormat('jpg', 'image/jpeg');
+        $request->setFormat('gif', 'image/gif');
+    }
+}

+ 4 - 0
src/Symfony/Bundle/AsseticBundle/Resources/config/controller.xml

@@ -9,6 +9,7 @@
         <parameter key="assetic.routing_loader.class">Symfony\Bundle\AsseticBundle\Routing\AsseticLoader</parameter>
         <parameter key="assetic.cache.class">Assetic\Cache\FilesystemCache</parameter>
         <parameter key="assetic.use_controller_worker.class">Symfony\Bundle\AsseticBundle\Factory\Worker\UseControllerWorker</parameter>
+        <parameter key="assetic.request_listener.class">Symfony\Bundle\AsseticBundle\Listener\RequestListener</parameter>
     </parameters>
 
     <services>
@@ -27,5 +28,8 @@
         <service id="assetic.use_controller_worker" class="%assetic.use_controller_worker.class%" public="false">
             <tag name="assetic.factory_worker" />
         </service>
+        <service id="assetic.request_listener" class="%assetic.request_listener.class%">
+            <tag name="kernel.listener" event="onCoreRequest" />
+        </service>
     </services>
 </container>

+ 12 - 0
src/Symfony/Bundle/AsseticBundle/Resources/config/schema/assetic-1.0.xsd

@@ -11,6 +11,7 @@
         <xsd:sequence>
             <xsd:element name="bundle" type="bundle" minOccurs="0" maxOccurs="unbounded" />
             <xsd:element name="filter" type="filter" minOccurs="0" maxOccurs="unbounded" />
+            <xsd:element name="twig" type="twig" minOccurs="0" maxOccurs="unbounded" />
         </xsd:sequence>
         <xsd:attribute name="debug" type="xsd:string" />
         <xsd:attribute name="use-controller" type="xsd:string" />
@@ -32,4 +33,15 @@
         <xsd:attribute name="resource" type="xsd:string" />
         <xsd:anyAttribute namespace="##any" processContents="lax" />
     </xsd:complexType>
+
+    <xsd:complexType name="twig">
+        <xsd:sequence>
+            <xsd:element name="function" type="twig_function" minOccurs="0" maxOccurs="unbounded" />
+        </xsd:sequence>
+    </xsd:complexType>
+
+    <xsd:complexType name="twig_function">
+        <xsd:attribute name="name" type="xsd:string" use="required" />
+        <xsd:anyAttribute namespace="##any" processContents="lax" />
+    </xsd:complexType>
 </xsd:schema>

+ 1 - 1
src/Symfony/Bundle/AsseticBundle/Resources/config/templating_twig.xml

@@ -14,8 +14,8 @@
             <tag name="twig.extension" />
             <tag name="assetic.templating.twig" />
             <argument type="service" id="assetic.asset_factory" />
-            <argument>%assetic.debug%</argument>
             <argument>%assetic.use_controller%</argument>
+            <argument type="collection" />
         </service>
         <service id="assetic.twig_formula_loader" class="%assetic.cached_formula_loader.class%" public="false">
             <tag name="assetic.formula_loader" alias="twig" />

+ 6 - 0
src/Symfony/Bundle/AsseticBundle/Tests/Resources/config/config.yml

@@ -20,3 +20,9 @@ assetic:
     use_controller: true
     read_from:      "%kernel.root_dir%/web"
     bundles:        [TestBundle]
+    filters:
+        yui_css:
+            jar: %kernel.root_dir/java/yui-compressor-2.4.6.jar
+    twig:
+        functions:
+            yui_css: { output: css/*.css }

+ 7 - 2
src/Symfony/Bundle/AsseticBundle/Twig/AsseticExtension.php

@@ -23,9 +23,9 @@ class AsseticExtension extends BaseAsseticExtension
 {
     private $useController;
 
-    public function __construct(AssetFactory $factory, $debug = false, $useController = false)
+    public function __construct(AssetFactory $factory, $useController = false, $functions = array())
     {
-        parent::__construct($factory, $debug);
+        parent::__construct($factory, $functions);
 
         $this->useController = $useController;
     }
@@ -39,6 +39,11 @@ class AsseticExtension extends BaseAsseticExtension
         );
     }
 
+    public function getNodeVisitors()
+    {
+        return array(new AsseticNodeVisitor());
+    }
+
     public function getGlobals()
     {
         $globals = parent::getGlobals();

+ 97 - 0
src/Symfony/Bundle/AsseticBundle/Twig/AsseticNodeVisitor.php

@@ -0,0 +1,97 @@
+<?php
+
+/*
+ * This file is part of the Symfony framework.
+ *
+ * (c) Fabien Potencier <fabien@symfony.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\AsseticFilterFunction;
+
+/**
+ * Assetic node visitor.
+ *
+ * @author Kris Wallsmith <kris@symfony.com>
+ */
+class AsseticNodeVisitor implements \Twig_NodeVisitorInterface
+{
+    public function enterNode(\Twig_NodeInterface $node, \Twig_Environment $env)
+    {
+        return $node;
+    }
+
+    public function leaveNode(\Twig_NodeInterface $node, \Twig_Environment $env)
+    {
+        if (!$formula = $this->checkNode($node, $env)) {
+            return $node;
+        }
+
+        list($input, $filters, $options) = $formula;
+        $line = $node->getLine();
+
+        // check context and call either asset() or path()
+        return new \Twig_Node_Expression_Conditional(
+            new \Twig_Node_Expression_GetAttr(
+                new \Twig_Node_Expression_Name('assetic', $line),
+                new \Twig_Node_Expression_Constant('use_controller', $line),
+                new \Twig_Node(),
+                \Twig_TemplateInterface::ARRAY_CALL,
+                $line
+            ),
+            new \Twig_Node_Expression_Function(
+                new \Twig_Node_Expression_Name('path', $line),
+                new \Twig_Node(array(
+                    new \Twig_Node_Expression_Constant('_assetic_'.$options['name'], $line),
+                )),
+                $line
+            ),
+            new \Twig_Node_Expression_Function(
+                new \Twig_Node_Expression_Name('asset', $line),
+                new \Twig_Node(array($node, new \Twig_Node_Expression_Constant(isset($options['package']) ? $options['package'] : null, $line))),
+                $line
+            ),
+            $line
+        );
+    }
+
+    /**
+     * Extracts formulae from filter function nodes.
+     *
+     * @return array|null The formula
+     */
+    private function checkNode(\Twig_NodeInterface $node, \Twig_Environment $env)
+    {
+        if ($node instanceof \Twig_Node_Expression_Function) {
+            $name = $node->getNode('name')->getAttribute('name');
+            if ($env->getFunction($name) instanceof AsseticFilterFunction) {
+                $arguments = array();
+                foreach ($node->getNode('arguments') as $argument) {
+                    $arguments[] = eval('return '.$env->compile($argument).';');
+                }
+
+                $invoker = $env->getExtension('assetic')->getFilterInvoker($name);
+                $factory = $invoker->getFactory();
+
+                $inputs = isset($arguments[0]) ? (array) $arguments[0] : array();
+                $filters = $invoker->getFilters();
+                $options = array_replace($invoker->getOptions(), isset($arguments[1]) ? $arguments[1] : array());
+
+                if (!isset($options['name'])) {
+                    $options['name'] = $factory->generateAssetName($inputs, $filters);
+                }
+
+                return array($inputs, $filters, $options);
+            }
+        }
+    }
+
+    public function getPriority()
+    {
+        return 0;
+    }
+}