SlideShare a Scribd company logo
Introducing Assetic
  Asset Management for PHP 5.3




         March 4, 2011
@kriswallsmith

•   Symfony Guru at

•   Symfony core team member

•   Doctrine contributor

•   10+ years experience with PHP and web development

•   Open source evangelist and international speaker
OpenSky connects you with innovators,
trendsetters and tastemakers.You choose
   the ones you like and each week they
  invite you to their private online sales.
OpenSky connects you with innovators,
trendsetters and tastemakers.You choose
   the ones you like and each week they
  invite you to their private online sales.
ShopOpenSky.com

•   PHP 5.3 + Symfony2

•   MongoDB + Doctrine MongoDB ODM

•   MySQL + Doctrine2 ORM

•   Less CSS

•   jQuery
Symfony2 is FAST
But you can still f*** that up
We build tools that
encourage best practices
Best practices like…
•   Dependency injection (DI)

•   Proper caching, edge side includes (ESI)

•   Test-driven development (TDD)

•   Don't repeat yourself (DRY)

•   Keep it simple, SVP (KISS)

•   Performance
If you haven’t optimized your
frontend, you haven’t optimized
Get your assets in line.
A poorly optimized frontend
     can destroy UX
…and SEO!


http://googlewebmastercentral.blogspot.com/2010/04/using-site-speed-in-web-search-ranking.html
Asset Management
Lots of awesome tools:
Lots of awesome tools:
•   CoffeeScript              •   Packer

•   Compass Framework         •   SASS

•   CSSEmbed                  •   Sprockets

•   Google Closure Compiler   •   Stylus

•   JSMin                     •   YUI Compressor

•   LESS
The ones written in PHP…
The ones written in PHP…
This is a difficult problem
Assetic makes it easy
as•cet•i•cism
describes a lifestyle characterized by abstinence from various sorts of worldly
     pleasures often with the aim of pursuing religious and spiritual goals
No B.S.
Enough talk
# /path/to/web/js/core.php

$core = new FileAsset('/path/to/jquery.js');
$core->load();

header('Content-Type: text/javascript');
echo $core->dump();
# /path/to/web/js/core.php

$core = new AssetCollection(array(
    new FileAsset('/path/to/jquery.js'),
    new GlobAsset('/path/to/js/core/*.js'),
));
$core->load();

header('Content-Type: text/javascript');
echo $core->dump();
# /path/to/web/js/core.php

$core = new AssetCollection(array(
    new FileAsset('/path/to/jquery.js'),
    new GlobAsset('/path/to/js/core/*.js'),
));
$core->load();many files into one == fewer HTTP requests
         Merge
header('Content-Type: text/javascript');
echo $core->dump();
# /path/to/web/js/core.php

$core = new AssetCollection(array(
    new FileAsset('/path/to/jquery.js'),
    new GlobAsset('/path/to/js/core/*.js'),
), array(
    new YuiCompressorJsFilter('/path/to/yui.jar'),
));
$core->load();

header('Content-Type: text/javascript');
echo $core->dump();
# /path/to/web/js/core.php

$core = new AssetCollection(array(
    new FileAsset('/path/to/jquery.js'),
    new GlobAsset('/path/to/js/core/*.js'),
), array(
    new YuiCompressorJsFilter('/path/to/yui.jar'),
));
$core->load();
   Compress the merged asset == less data over the wire

header('Content-Type: text/javascript');
echo $core->dump();
<script src="js/core.php"></script>
Assetic is
Assets & Filters
Inspired by Python’s webassets


        https://github.com/miracle2k/webassets
Assets have lazy, mutable content
A filter acts on an asset’s contents
    during “load” and “dump”
Assets can be gathered in
       collections
A collection is an asset
Assetic (Symfony Live Paris)
Asset
Filter
Asset
Filter
Filter
Asset
Load
   Filter
   Filter
   Asset
Filter
Filter
Asset




         Dump
Filter
Filter
Asset
Asset Collection
Filter              Filter
Filter              Filter
Asset               Asset
Asset Collection   Filter
                                 Filter
     Asset Collection            Asset
Filter              Filter
Filter              Filter
Asset               Asset
                                 Filter
                                 Filter
                                 Asset
# /path/to/web/css/styles.php

$styles = new FileAsset('/path/to/main.sass', array(
    new SassFilter(),
));

header('Content-Type: text/css');
echo $styles->dump();
# /path/to/web/css/styles.php

$styles = new FileAsset('/path/to/main.sass', array(
    new SassFilter(),
));
      Load is implied
header('Content-Type: text/css');
echo $styles->dump();
# /path/to/web/css/styles.php

$styles = new AssetCollection(array(
    new FileAsset('/path/to/main.sass', array(
        new SassFilter(),
    )),
    new FileAsset('/path/to/more.css'),
));

header('Content-Type: text/css');
echo $styles->dump();
# /path/to/web/css/styles.php

$styles = new AssetCollection(array(
    new FileAsset('/path/to/main.sass', array(
        new SassFilter(),
    )),
    new FileAsset('/path/to/more.css'),
), array(
    new YuiCompressorCss('/path/to/yui.jar'),
));

header('Content-Type: text/css');
echo $styles->dump();
# /path/to/web/css/styles.php

$styles = new AssetCollection(array(
    new FileAsset('/path/to/main.sass', array(
          new SassFilter(),
    )),
    new FileAsset('/path/to/more.css'),
), array(
    new YuiCompressorCss('/path/to/yui.jar'),
));
     Lazy! The filesystem isn't touched until now
header('Content-Type: text/css');
echo $styles->dump();
Basic Asset Classes

•   AssetCollection

•   AssetReference

•   FileAsset

•   GlobAsset

•   StringAsset
Core Filter Classes
•   CallablesFilter                   •   SassScssFilter

•   CoffeeScriptFilter                •   SprocketsFilter

•   CssRewriteFilter                  •   StylusFilter

•   GoogleClosureCompilerApiFilter   •   YuiCssCompressorFilter

•   GoogleClosureCompilerJarFilter   •   YuiJsCompressorFilter

•   LessFilter                        •   More to come…

•   SassSassFilter
Asset Manager
$am = new AssetManager();
$am->set('jquery',
    new FileAsset('/path/to/jquery.js'));
$plugin = new AssetCollection(array(
    new AssetReference($am, 'jquery'),
    new FileAsset('/path/to/jquery.plugin.js'),
));
$core = new AssetCollection(array(
    $jquery,
    $plugin1,
    $plugin2,
));

header('text/javascript');
echo $core->dump();
jQuery will only be included once
       $core = new AssetCollection(array(
           $jquery,
           $plugin1,
           $plugin2,
       ));

       header('text/javascript');
       echo $core->dump();
Filter Manager
$yui = new YuiCompressorJs();
$yui->setNomunge(true);

$fm = new FilterManager();
$fm->set('yui_js', $yui);
$jquery = new FileAsset('/path/to/core.js');
$jquery->ensureFilter($fm->get('yui_js'));

$core = new AssetCollection(array(
    $jquery,
    new GlobAsset('/path/to/js/core/*.js'),
));
$core->ensureFilter($fm->get('yui_js'));
jQuery will only be compressed once

   $jquery = new FileAsset('/path/to/core.js');
   $jquery->ensureFilter($fm->get('yui_js'));

   $core = new AssetCollection(array(
       $jquery,
       new GlobAsset('/path/to/js/core/*.js'),
   ));
   $core->ensureFilter($fm->get('yui_js'));
Asset Factory
$fm = new FilterManager();
$fm->set('coffee', new CoffeeScriptFilter());
$fm->set('closure', new ClosureFilter());

$factory = new AssetFactory('/path/to/web');
$factory->setFilterManager($fm);
$asset = $factory->createAsset(
    array('js/src/*.coffee'),
    array('coffee', 'closure')
);

header('Content-Type: text/javascript');
echo $asset->dump();
Debug Mode
Debugging compressed
   Javascript sucks
Mark filters for omission
in debug mode using a “?”
// new AssetFactory('/path/to/web', $debug = true);

$asset = $factory->createAsset(
    array('js/src/*.coffee'),
    array('coffee', 'closure')
);

header('Content-Type: text/javascript');
echo $asset->dump();
// new AssetFactory('/path/to/web', true);

$asset = $factory->createAsset(
    array('js/src/*.coffee'),
    array('coffee', '?closure')
);

header('Content-Type: text/javascript');
echo $asset->dump();
// new AssetFactory('/path/to/web', false);

$asset = $factory->createAsset(
    array('js/src/*.coffee'),
    array('coffee', '?closure'),
    array('debug' => true)
);

header('Content-Type: text/javascript');
echo $asset->dump();
Good: Basic Caching
# /path/to/web/css/styles.php

$styles = new AssetCollection(
    array(new FileAsset('/path/to/main.sass')),
    array(new SassFilter())
);

echo $styles->dump();
# /path/to/web/css/styles.php

$styles = new AssetCache(new AssetCollection(
    array(new FileAsset('/path/to/main.sass')),
    array(new SassFilter())
), new FilesystemCache('/path/to/cache'));

echo $styles->dump();
# /path/to/web/css/styles.php

$styles = new AssetCache(new AssetCollection(
    array(new FileAsset('/path/to/main.sass')),
    array(new SassFilter())
), new FilesystemCache('/path/to/cache'));
          Run the filters once and cache the content

echo $styles->dump();
Better: HTTP Caching
// $core = new AssetCache(...

$mtime = gmdate('D, d M y H:i:s',
    $core->getLastModified()).' GMT';

if ($mtime == $_SERVER['HTTP_IF_MODIFIED_SINCE']) {
    header('HTTP/1.0 304 Not Modified');
    exit();
}

header('Content-Type: text/javascript');
header('Last-Modified: '.$mtime);
echo $core->dump();
Best: Static Assets
# /path/to/scripts/dump_assets.php

$am = new AssetManager();
$am->set('foo', $foo);
// etc...

$writer = new AssetWriter('/path/to/web');
$writer->writeManagerAssets($am);
Best-est:
Content Distribution Network
new AssetWriter('s3://my-bucket')
new AssetWriter('s3://my-bucket')

                 A CloudFront S3 bucket
Custom Stream Wrappers


$s3 = new Zend_Service_Amazon_S3($key, $secret);
$s3->registerStreamWrapper();
Not Lazy Enough?
Asset Formulae and the
 Lazy Asset Manager
$asset = $factory->createAsset(
    array('js/src/*.coffee'),
    array('coffee', '?closure'),
    array('output' => 'js/*.js')
);
$formula = array(
    array('js/src/*.coffee'),
    array('coffee', '?closure'),
    array('output' => 'js/*.js')
);
$am = new LazyAssetManager($factory);
$am->setFormula('core_js', $formula);

header('Content-Type: text/javascript');
echo $am->get('core_js')->dump();
A Thought
Assets are a part of the view layer
  and should be defined there.
<!-- header.php -->

<?php foreach (assetic_javascripts(
    array('js/core.js', 'js/more.js'),
    array('?yui_js')) as $url): ?>

<script src="<?php echo $url ?>"></script>

<?php endforeach; ?>
An Issue
Assets defined in the view layer
must actually exist somewhere
Option Number Bad
Lazily dump assets to the
      web directory
Option Number Good
Eagerly dump assets to the
      web directory
A template is a configuration file
Formula Loaders
extract asset formulae from templates
$loader = new FunctionCallsFormulaLoader();
$resource = new DirectoryResource(
    '/path/to/templates',
    '/.php$/'
);

$formulae = $loader->load($resource);
$am = new LazyAssetManager($factory);
$am->setLoader('php', $loader);
$am->addResource($resource, 'php');

$writer = new AssetWriter('/path/to/web');
$writer->writeManagerAssets($am);
$am = new LazyAssetManager($factory);
$am->setLoader('php', $loader);
$am->addResource($resource, 'php');
               Expensive every time
$writer = new AssetWriter('/path/to/web');
$writer->writeManagerAssets($am);
$cache = new ConfigCache('/path/to/cache');

$loader = new CachedFormulaLoader(
    $loader,
    $cache,
    $debug
);
$cache = new ConfigCache('/path/to/cache');

$loader = new CachedFormulaLoader(
    $loader,
    $cache,
    $debug
);
    Whether to stat each file for changes
Twig Integration
$twig->addExtension(new AsseticExtension($factory));
{% assetic 'js/*.coffee' filter='coffee' %}
<script src="{{ asset_url }}"></script>
{% endassetic %}
<script src="assets/92429d8"></script>
{% assetic 'js/*.coffee' filter='coffee' %}
<script src="{{ asset_url }}"></script>
{% endassetic %}
{% assetic 'js/*.coffee' filter='coffee' output='js/*.js' %}
<script src="{{ asset_url }}"></script>
{% endassetic %}
<script src="js/92429d8.js"></script>
{% assetic 'js/*.coffee' filter='coffee' output='js/*.js' %}
<script src="{{ asset_url }}"></script>
{% endassetic %}
{% javascripts 'js/*.coffee' filter='coffee,?closure' %}
<script src="{{ asset_url }}"></script>
{% endjavascripts %}
{% javascripts 'js/*.coffee' filter='coffee,?closure' %}
<script src="{{ asset_url }}"></script>
{% endjavascripts %}


      Adds a default output string
{% javascripts 'js/*.coffee' filter='coffee,?closure'
   debug=true %}
<script src="{{ asset_url }}"></script>
{% endjavascripts %}
<script src="js/92429d8_1.js"></script>
<script src="js/92429d8_2.js"></script>
<script src="js/92429d8_3.js"></script>
Each "leaf" asset is referenced individually

<script src="js/92429d8_1.js"></script>
<script src="js/92429d8_2.js"></script>
<script src="js/92429d8_3.js"></script>
AsseticBundle
  Symfony2 integration
{% assetic filter='scss,?yui_css', output='css/all.css',
   '@MainBundle/Resources/sass/main.scss',
   '@AnotherBundle/Resources/sass/more.scss' %}
<link href="{{ asset_url }}" rel="stylesheet" />
{% endassetic %}
<link href="css/all.css" rel="stylesheet" />
Configuration
assetic:
    debug:            %kernel.debug%
    use_controller:   %kernel.debug%
    read_from:        %kernel.root_dir%/../web
    write_to:         s3://mybucket
{# when use_controller=true #}

<script src="{{ path('assetic_foo') }}"...
# routing_dev.yml
_assetic:
    resource: .
    type:     assetic
{# when use_controller=false #}

<script src="{{ asset('js/core.js') }}"></script>
{# when use_controller=false #}

<script src="{{ asset('js/core.js') }}"></script>

                 Lots for free
The Symfony2 Assets Helper


•   Multiple asset domains

•   Cache buster
framework:
    templating:
        assets_version: 1.2.3
        assets_base_urls:
            - http://assets1.domain.com
            - http://assets2.domain.com
            - http://assets3.domain.com
            - http://assets4.domain.com
{% assetic filter='scss,?yui_css', output='css/all.css',
   '@MainBundle/Resources/sass/main.scss',
   '@AnotherBundle/Resources/sass/more.scss' %}
<link href="{{ asset_url }}" rel="stylesheet" />
{% endassetic %}
<link href="http://assets3.domain.com/css/all.css?1.2.3" ...
assetic:dump
$ php app/console assetic:dump web/
$ php app/console assetic:dump s3://my-bucket
assetic:dump --watch
  Dump static assets in the background as you develop
Questions?
http://github.com/kriswallsmith/assetic

More Related Content

PDF
Introducing Assetic: Asset Management for PHP 5.3
PDF
Assetic (Zendcon)
PDF
Assetic (OSCON)
PDF
Django - 次の一歩 gumiStudy#3
PDF
Moving from Django Apps to Services
KEY
[Coscup 2012] JavascriptMVC
PDF
深入淺出 MVC
PDF
The effective use of Django ORM
Introducing Assetic: Asset Management for PHP 5.3
Assetic (Zendcon)
Assetic (OSCON)
Django - 次の一歩 gumiStudy#3
Moving from Django Apps to Services
[Coscup 2012] JavascriptMVC
深入淺出 MVC
The effective use of Django ORM

What's hot (20)

KEY
PDF
Introduction To Django (Strange Loop 2011)
PDF
HTTP Caching and PHP
PDF
Keeping it Small: Getting to know the Slim Micro Framework
PDF
Building Modern and Secure PHP Applications – Codementor Office Hours with Be...
KEY
Phpne august-2012-symfony-components-friends
PDF
The Best (and Worst) of Django
KEY
PHP API
PDF
HTML5 JavaScript APIs
PPTX
Let's write secure drupal code! - Drupal Camp Pannonia 2019
PPT
Dance for the puppet master: G6 Tech Talk
PDF
WebGUI Developers Workshop
PPTX
10 Things Every Plugin Developer Should Know (WordCamp Atlanta 2013)
KEY
CodeIgniter 3.0
PDF
Symfony Guard Authentication: Fun with API Token, Social Login, JWT and more
PDF
Getting to The Loop - London Wordpress Meetup July 28th
PDF
Refresh Austin - Intro to Dexy
PDF
Drupal, meet Assetic
PPTX
Let's write secure Drupal code! DUG Belgium - 08/08/2019
PPT
Render API - Pavel Makhrinsky
Introduction To Django (Strange Loop 2011)
HTTP Caching and PHP
Keeping it Small: Getting to know the Slim Micro Framework
Building Modern and Secure PHP Applications – Codementor Office Hours with Be...
Phpne august-2012-symfony-components-friends
The Best (and Worst) of Django
PHP API
HTML5 JavaScript APIs
Let's write secure drupal code! - Drupal Camp Pannonia 2019
Dance for the puppet master: G6 Tech Talk
WebGUI Developers Workshop
10 Things Every Plugin Developer Should Know (WordCamp Atlanta 2013)
CodeIgniter 3.0
Symfony Guard Authentication: Fun with API Token, Social Login, JWT and more
Getting to The Loop - London Wordpress Meetup July 28th
Refresh Austin - Intro to Dexy
Drupal, meet Assetic
Let's write secure Drupal code! DUG Belgium - 08/08/2019
Render API - Pavel Makhrinsky
Ad

Viewers also liked (12)

PDF
How kris-writes-symfony-apps-london
PDF
The View From Inside
PDF
Introducing Assetic (NYPHP)
PDF
How Kris Writes Symfony Apps
PDF
Advanced symfony Techniques
PDF
Doctrine MongoDB ODM (PDXPHP)
PDF
Symfony 2
PDF
A Practical Introduction to Symfony2
PDF
How Kris Writes Symfony Apps
PDF
Matters of State
PDF
Symfony in the Cloud
PDF
Love and Loss: A Symfony Security Play
How kris-writes-symfony-apps-london
The View From Inside
Introducing Assetic (NYPHP)
How Kris Writes Symfony Apps
Advanced symfony Techniques
Doctrine MongoDB ODM (PDXPHP)
Symfony 2
A Practical Introduction to Symfony2
How Kris Writes Symfony Apps
Matters of State
Symfony in the Cloud
Love and Loss: A Symfony Security Play
Ad

Similar to Assetic (Symfony Live Paris) (20)

PPTX
Creating Operational Redundancy for Effective Web Data Mining
PPT
Intro to php
PPT
eZ Publish Cluster Unleashed
PPTX
The Way to Theme Enlightenment
PDF
How to make a WordPress theme
PDF
Understanding backbonejs
PPTX
The Way to Theme Enlightenment 2017
PPTX
Childthemes ottawa-word camp-1919
PDF
Building Lithium Apps
PDF
以Vue開發電子商務網站
架構與眉角
PPTX
On secure application of PHP wrappers
KEY
Unit testing with zend framework PHPBenelux
PDF
Laying the proper foundation for plugin and theme development
KEY
Introduction to jQuery - Barcamp London 9
ODP
Aura Project for PHP
PDF
Styling components with JavaScript
PDF
Railsbridge javascript
PPTX
HirshHorn theme: how I created it
PDF
Unit testing with zend framework tek11
PPTX
Windows Azure Storage & Sql Azure
Creating Operational Redundancy for Effective Web Data Mining
Intro to php
eZ Publish Cluster Unleashed
The Way to Theme Enlightenment
How to make a WordPress theme
Understanding backbonejs
The Way to Theme Enlightenment 2017
Childthemes ottawa-word camp-1919
Building Lithium Apps
以Vue開發電子商務網站
架構與眉角
On secure application of PHP wrappers
Unit testing with zend framework PHPBenelux
Laying the proper foundation for plugin and theme development
Introduction to jQuery - Barcamp London 9
Aura Project for PHP
Styling components with JavaScript
Railsbridge javascript
HirshHorn theme: how I created it
Unit testing with zend framework tek11
Windows Azure Storage & Sql Azure

Recently uploaded (20)

DOCX
The AUB Centre for AI in Media Proposal.docx
PDF
Empathic Computing: Creating Shared Understanding
PDF
Mobile App Security Testing_ A Comprehensive Guide.pdf
PDF
Spectral efficient network and resource selection model in 5G networks
PDF
Architecting across the Boundaries of two Complex Domains - Healthcare & Tech...
PPTX
Cloud computing and distributed systems.
PDF
Electronic commerce courselecture one. Pdf
PPTX
PA Analog/Digital System: The Backbone of Modern Surveillance and Communication
PPTX
KOM of Painting work and Equipment Insulation REV00 update 25-dec.pptx
PPTX
Understanding_Digital_Forensics_Presentation.pptx
PDF
Per capita expenditure prediction using model stacking based on satellite ima...
PDF
Bridging biosciences and deep learning for revolutionary discoveries: a compr...
PDF
Dropbox Q2 2025 Financial Results & Investor Presentation
PPTX
Effective Security Operations Center (SOC) A Modern, Strategic, and Threat-In...
PDF
Build a system with the filesystem maintained by OSTree @ COSCUP 2025
PDF
Machine learning based COVID-19 study performance prediction
PPT
Teaching material agriculture food technology
PDF
Shreyas Phanse Resume: Experienced Backend Engineer | Java • Spring Boot • Ka...
PPTX
Digital-Transformation-Roadmap-for-Companies.pptx
PDF
Building Integrated photovoltaic BIPV_UPV.pdf
The AUB Centre for AI in Media Proposal.docx
Empathic Computing: Creating Shared Understanding
Mobile App Security Testing_ A Comprehensive Guide.pdf
Spectral efficient network and resource selection model in 5G networks
Architecting across the Boundaries of two Complex Domains - Healthcare & Tech...
Cloud computing and distributed systems.
Electronic commerce courselecture one. Pdf
PA Analog/Digital System: The Backbone of Modern Surveillance and Communication
KOM of Painting work and Equipment Insulation REV00 update 25-dec.pptx
Understanding_Digital_Forensics_Presentation.pptx
Per capita expenditure prediction using model stacking based on satellite ima...
Bridging biosciences and deep learning for revolutionary discoveries: a compr...
Dropbox Q2 2025 Financial Results & Investor Presentation
Effective Security Operations Center (SOC) A Modern, Strategic, and Threat-In...
Build a system with the filesystem maintained by OSTree @ COSCUP 2025
Machine learning based COVID-19 study performance prediction
Teaching material agriculture food technology
Shreyas Phanse Resume: Experienced Backend Engineer | Java • Spring Boot • Ka...
Digital-Transformation-Roadmap-for-Companies.pptx
Building Integrated photovoltaic BIPV_UPV.pdf

Assetic (Symfony Live Paris)