The Asset Component

The Asset component manages URL generation and versioning of web assets suchas CSS stylesheets, JavaScript files and image files.

In the past, it was common for web applications to hardcode URLs of web assets.For example:

  1. <link rel="stylesheet" type="text/css" href="/css/main.css">
  2.  
  3. <!-- ... -->
  4.  
  5. <a href="/"><img src="/images/logo.png"></a>

This practice is no longer recommended unless the web application is extremelysimple. Hardcoding URLs can be a disadvantage because:

  • Templates get verbose: you have to write the full path for eachasset. When using the Asset component, you can group assets in packages toavoid repeating the common part of their path;
  • Versioning is difficult: it has to be custom managed for eachapplication. Adding a version (e.g. main.css?v=5) to the asset URLsis essential for some applications because it allows you to control howthe assets are cached. The Asset component allows you to define differentversioning strategies for each package;
  • Moving assets' location is cumbersome and error-prone: it requires you tocarefully update the URLs of all assets included in all templates. The Assetcomponent allows to move assets effortlessly just by changing the base pathvalue associated with the package of assets;
  • It's nearly impossible to use multiple CDNs: this technique requiresyou to change the URL of the asset randomly for each request. The Asset componentprovides out-of-the-box support for any number of multiple CDNs, both regular(http://) and secure (https://).

Installation

  1. $ composer require symfony/asset

Note

If you install this component outside of a Symfony application, you mustrequire the vendor/autoload.php file in your code to enable the classautoloading mechanism provided by Composer. Readthis article for more details.

Usage

Asset Packages

The Asset component manages assets through packages. A package groups all theassets which share the same properties: versioning strategy, base path, CDN hosts,etc. In the following basic example, a package is created to manage assets withoutany versioning:

  1. use Symfony\Component\Asset\Package;
  2. use Symfony\Component\Asset\VersionStrategy\EmptyVersionStrategy;
  3.  
  4. $package = new Package(new EmptyVersionStrategy());
  5.  
  6. // Absolute path
  7. echo $package->getUrl('/image.png');
  8. // result: /image.png
  9.  
  10. // Relative path
  11. echo $package->getUrl('image.png');
  12. // result: image.png

Packages implement PackageInterface,which defines the following two methods:

Versioned Assets

One of the main features of the Asset component is the ability to managethe versioning of the application's assets. Asset versions are commonly usedto control how these assets are cached.

Instead of relying on a simple version mechanism, the Asset component allowsyou to define advanced versioning strategies via PHP classes. The two built-instrategies are the EmptyVersionStrategy,which doesn't add any version to the asset and StaticVersionStrategy,which allows you to set the version with a format string.

In this example, the StaticVersionStrategy is used to append the v1suffix to any asset path:

  1. use Symfony\Component\Asset\Package;
  2. use Symfony\Component\Asset\VersionStrategy\StaticVersionStrategy;
  3.  
  4. $package = new Package(new StaticVersionStrategy('v1'));
  5.  
  6. // Absolute path
  7. echo $package->getUrl('/image.png');
  8. // result: /image.png?v1
  9.  
  10. // Relative path
  11. echo $package->getUrl('image.png');
  12. // result: image.png?v1

In case you want to modify the version format, pass a sprintf-compatible formatstring as the second argument of the StaticVersionStrategy constructor:

  1. // puts the 'version' word before the version value
  2. $package = new Package(new StaticVersionStrategy('v1', '%s?version=%s'));
  3.  
  4. echo $package->getUrl('/image.png');
  5. // result: /image.png?version=v1
  6.  
  7. // puts the asset version before its path
  8. $package = new Package(new StaticVersionStrategy('v1', '%2$s/%1$s'));
  9.  
  10. echo $package->getUrl('/image.png');
  11. // result: /v1/image.png
  12.  
  13. echo $package->getUrl('image.png');
  14. // result: v1/image.png

JSON File Manifest

A popular strategy to manage asset versioning, which is used by tools such asWebpack, is to generate a JSON file mapping all source file names to theircorresponding output file:

  1. // rev-manifest.json
  2. {
  3. "css/app.css": "build/css/app.b916426ea1d10021f3f17ce8031f93c2.css",
  4. "js/app.js": "build/js/app.13630905267b809161e71d0f8a0c017b.js",
  5. "...": "..."
  6. }

In those cases, use theJsonManifestVersionStrategy:

  1. use Symfony\Component\Asset\Package;
  2. use Symfony\Component\Asset\VersionStrategy\JsonManifestVersionStrategy;
  3.  
  4. $package = new Package(new JsonManifestVersionStrategy(__DIR__.'/rev-manifest.json'));
  5.  
  6. echo $package->getUrl('css/app.css');
  7. // result: build/css/app.b916426ea1d10021f3f17ce8031f93c2.css

Custom Version Strategies

Use the VersionStrategyInterfaceto define your own versioning strategy. For example, your application may needto append the current date to all its web assets in order to bust the cacheevery day:

  1. use Symfony\Component\Asset\VersionStrategy\VersionStrategyInterface;
  2.  
  3. class DateVersionStrategy implements VersionStrategyInterface
  4. {
  5. private $version;
  6.  
  7. public function __construct()
  8. {
  9. $this->version = date('Ymd');
  10. }
  11.  
  12. public function getVersion($path)
  13. {
  14. return $this->version;
  15. }
  16.  
  17. public function applyVersion($path)
  18. {
  19. return sprintf('%s?v=%s', $path, $this->getVersion($path));
  20. }
  21. }

Grouped Assets

Often, many assets live under a common path (e.g. /static/images). Ifthat's your case, replace the default Packageclass with PathPackage to avoid repeatingthat path over and over again:

  1. use Symfony\Component\Asset\PathPackage;
  2. // ...
  3.  
  4. $pathPackage = new PathPackage('/static/images', new StaticVersionStrategy('v1'));
  5.  
  6. echo $pathPackage->getUrl('logo.png');
  7. // result: /static/images/logo.png?v1
  8.  
  9. // Base path is ignored when using absolute paths
  10. echo $pathPackage->getUrl('/logo.png');
  11. // result: /logo.png?v1

Request Context Aware Assets

If you are also using the HttpFoundationcomponent in your project (for instance, in a Symfony application), the PathPackageclass can take into account the context of the current request:

  1. use Symfony\Component\Asset\Context\RequestStackContext;
  2. use Symfony\Component\Asset\PathPackage;
  3. // ...
  4.  
  5. $pathPackage = new PathPackage(
  6. '/static/images',
  7. new StaticVersionStrategy('v1'),
  8. new RequestStackContext($requestStack)
  9. );
  10.  
  11. echo $pathPackage->getUrl('logo.png');
  12. // result: /somewhere/static/images/logo.png?v1
  13.  
  14. // Both "base path" and "base url" are ignored when using absolute path for asset
  15. echo $pathPackage->getUrl('/logo.png');
  16. // result: /logo.png?v1

Now that the request context is set, the PathPackage will prepend thecurrent request base URL. So, for example, if your entire site is hosted underthe /somewhere directory of your web server root directory and the configuredbase path is /static/images, all paths will be prefixed with/somewhere/static/images.

Absolute Assets and CDNs

Applications that host their assets on different domains and CDNs (ContentDelivery Networks) should use the UrlPackageclass to generate absolute URLs for their assets:

  1. use Symfony\Component\Asset\UrlPackage;
  2. // ...
  3.  
  4. $urlPackage = new UrlPackage(
  5. 'http://static.example.com/images/',
  6. new StaticVersionStrategy('v1')
  7. );
  8.  
  9. echo $urlPackage->getUrl('/logo.png');
  10. // result: http://static.example.com/images/logo.png?v1

You can also pass a schema-agnostic URL:

  1. use Symfony\Component\Asset\UrlPackage;
  2. // ...
  3.  
  4. $urlPackage = new UrlPackage(
  5. '//static.example.com/images/',
  6. new StaticVersionStrategy('v1')
  7. );
  8.  
  9. echo $urlPackage->getUrl('/logo.png');
  10. // result: //static.example.com/images/logo.png?v1

This is useful because assets will automatically be requested via HTTPS ifa visitor is viewing your site in https. If you want to use this, make surethat your CDN host supports HTTPS.

In case you serve assets from more than one domain to improve applicationperformance, pass an array of URLs as the first argument to the UrlPackageconstructor:

  1. use Symfony\Component\Asset\UrlPackage;
  2. // ...
  3.  
  4. $urls = [
  5. '//static1.example.com/images/',
  6. '//static2.example.com/images/',
  7. ];
  8. $urlPackage = new UrlPackage($urls, new StaticVersionStrategy('v1'));
  9.  
  10. echo $urlPackage->getUrl('/logo.png');
  11. // result: http://static1.example.com/images/logo.png?v1
  12. echo $urlPackage->getUrl('/icon.png');
  13. // result: http://static2.example.com/images/icon.png?v1

For each asset, one of the URLs will be randomly used. But, the selectionis deterministic, meaning that each asset will always be served by the samedomain. This behavior simplifies the management of HTTP cache.

Request Context Aware Assets

Similarly to application-relative assets, absolute assets can also take intoaccount the context of the current request. In this case, only the requestscheme is considered, in order to select the appropriate base URL (HTTPs orprotocol-relative URLs for HTTPs requests, any base URL for HTTP requests):

  1. use Symfony\Component\Asset\Context\RequestStackContext;
  2. use Symfony\Component\Asset\UrlPackage;
  3. // ...
  4.  
  5. $urlPackage = new UrlPackage(
  6. ['http://example.com/', 'https://example.com/'],
  7. new StaticVersionStrategy('v1'),
  8. new RequestStackContext($requestStack)
  9. );
  10.  
  11. echo $urlPackage->getUrl('/logo.png');
  12. // assuming the RequestStackContext says that we are on a secure host
  13. // result: https://example.com/logo.png?v1

Named Packages

Applications that manage lots of different assets may need to group them inpackages with the same versioning strategy and base path. The Asset componentincludes a Packages class to simplifymanagement of several packages.

In the following example, all packages use the same versioning strategy, butthey all have different base paths:

  1. use Symfony\Component\Asset\Package;
  2. use Symfony\Component\Asset\Packages;
  3. use Symfony\Component\Asset\PathPackage;
  4. use Symfony\Component\Asset\UrlPackage;
  5. // ...
  6.  
  7. $versionStrategy = new StaticVersionStrategy('v1');
  8.  
  9. $defaultPackage = new Package($versionStrategy);
  10.  
  11. $namedPackages = [
  12. 'img' => new UrlPackage('http://img.example.com/', $versionStrategy),
  13. 'doc' => new PathPackage('/somewhere/deep/for/documents', $versionStrategy),
  14. ];
  15.  
  16. $packages = new Packages($defaultPackage, $namedPackages);

The Packages class allows to define a default package, which will be appliedto assets that don't define the name of package to use. In addition, thisapplication defines a package named img to serve images from an externaldomain and a doc package to avoid repeating long paths when linking to adocument inside a template:

  1. echo $packages->getUrl('/main.css');
  2. // result: /main.css?v1
  3.  
  4. echo $packages->getUrl('/logo.png', 'img');
  5. // result: http://img.example.com/logo.png?v1
  6.  
  7. echo $packages->getUrl('resume.pdf', 'doc');
  8. // result: /somewhere/deep/for/documents/resume.pdf?v1

Local Files and Other Protocols

In addition to HTTP this component supports other protocols (such as file://and ftp://). This allows for example to serve local files in order toimprove performance:

  1. use Symfony\Component\Asset\UrlPackage;
  2. // ...
  3.  
  4. $localPackage = new UrlPackage(
  5. 'file:///path/to/images/',
  6. new EmptyVersionStrategy()
  7. );
  8.  
  9. $ftpPackage = new UrlPackage(
  10. 'ftp://example.com/images/',
  11. new EmptyVersionStrategy()
  12. );
  13.  
  14. echo $localPackage->getUrl('/logo.png');
  15. // result: file:///path/to/images/logo.png
  16.  
  17. echo $ftpPackage->getUrl('/logo.png');
  18. // result: ftp://example.com/images/logo.png

Learn more