The Asset Component ¶
The Asset component manages URL generation and versioning of web assets such as CSS stylesheets, JavaScript files and image files.
Asset コンポーネントは、CSS スタイルシート、JavaScript ファイル、画像ファイルなどの Web アセットの URL 生成とバージョン管理を管理します。
In the past, it was common for web applications to hard-code the URLs of web assets. For example:
1 2 3 4 5 |
<link rel="stylesheet" type="text/css" href="/css/main.css">
<!-- ... -->
<a href="/"><img src="/images/logo.png" alt="logo"></a>
|
This practice is no longer recommended unless the web application is extremely simple. Hardcoding URLs can be a disadvantage because:
- Templates get verbose: you have to write the full path for each
asset. When using the Asset component, you can group assets in packages to
avoid repeating the common part of their path;テンプレートは冗長になります。各アセットのフル パスを記述する必要があります。 Asset コンポーネントを使用すると、アセットをパッケージにグループ化して、パスの共通部分の繰り返しを避けることができます。
- Versioning is difficult: it has to be custom managed for each
application. Adding a version (e.g.
main.css?v=5
) to the asset URLs is essential for some applications because it allows you to control how the assets are cached. The Asset component allows you to define different versioning strategies for each package;バージョン管理は難しく、アプリケーションごとにカスタム管理する必要があります。アセットの URL にバージョン (main.css?v=5 など) を追加することは、アセットのキャッシュ方法を制御できるため、一部のアプリケーションにとって不可欠です。 Asset コンポーネントを使用すると、パッケージごとに異なるバージョン管理戦略を定義できます。 - Moving assets' location is cumbersome and error-prone: it requires you to
carefully update the URLs of all assets included in all templates. The Asset
component allows to move assets effortlessly just by changing the base path
value associated with the package of assets;アセットの場所を移動するのは面倒で、エラーが発生しやすくなります。すべてのテンプレートに含まれるすべてのアセットの URL を慎重に更新する必要があります。 Assetcomponent を使用すると、アセットのパッケージに関連付けられたベース パス値を変更するだけで、アセットを簡単に移動できます。
- It's nearly impossible to use multiple CDNs: this technique requires
you to change the URL of the asset randomly for each request. The Asset component
provides out-of-the-box support for any number of multiple CDNs, both regular
(
http://
) and secure (https://
).複数の CDN を使用することはほとんど不可能です。この手法では、リクエストごとにアセットの URL をランダムに変更する必要があります。 Asset コンポーネントは、標準 (http://) とセキュア (https://) の両方で、任意の数の複数の CDN をすぐにサポートします。
Installation ¶
1 |
$ composer require symfony/asset
|
Note
If you install this component outside of a Symfony application, you must
require the vendor/autoload.php
file in your code to enable the class
autoloading mechanism provided by Composer. Read
this article for more details.
Usage ¶
Asset Packages ¶
The Asset component manages assets through packages. A package groups all the assets which share the same properties: versioning strategy, base path, CDN hosts, etc. In the following basic example, a package is created to manage assets without any versioning:
1 2 3 4 5 6 7 8 9 10 11 12 |
use Symfony\Component\Asset\Package;
use Symfony\Component\Asset\VersionStrategy\EmptyVersionStrategy;
$package = new Package(new EmptyVersionStrategy());
// Absolute path
echo $package->getUrl('/image.png');
// result: /image.png
// Relative path
echo $package->getUrl('image.png');
// result: image.png
|
Packages implement PackageInterface, which defines the following two methods:
- getVersion()
-
Returns the asset version for an asset.
アセットのアセット バージョンを返します。
- getUrl()
-
Returns an absolute or root-relative public path.
絶対またはルート相対パブリック パスを返します。
With a package, you can:
A) version the assets;
B) set a common base path (e.g. /css
)
for the assets;
C) configure a CDN for the assets
Versioned Assets ¶
One of the main features of the Asset component is the ability to manage the versioning of the application's assets. Asset versions are commonly used to control how these assets are cached.
Instead of relying on a simple version mechanism, the Asset component allows you to define advanced versioning strategies via PHP classes. The two built-in strategies 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 v1
suffix to any asset path:
1 2 3 4 5 6 7 8 9 10 11 12 |
use Symfony\Component\Asset\Package;
use Symfony\Component\Asset\VersionStrategy\StaticVersionStrategy;
$package = new Package(new StaticVersionStrategy('v1'));
// Absolute path
echo $package->getUrl('/image.png');
// result: /image.png?v1
// Relative path
echo $package->getUrl('image.png');
// result: image.png?v1
|
In case you want to modify the version format, pass a sprintf
-compatible
format string as the second argument of the StaticVersionStrategy
constructor:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
// puts the 'version' word before the version value
$package = new Package(new StaticVersionStrategy('v1', '%s?version=%s'));
echo $package->getUrl('/image.png');
// result: /image.png?version=v1
// puts the asset version before its path
$package = new Package(new StaticVersionStrategy('v1', '%2$s/%1$s'));
echo $package->getUrl('/image.png');
// result: /v1/image.png
echo $package->getUrl('image.png');
// result: v1/image.png
|
JSON File Manifest ¶
A popular strategy to manage asset versioning, which is used by tools such as Webpack, is to generate a JSON file mapping all source file names to their corresponding output file:
1 2 3 4 5 |
{
"css/app.css": "build/css/app.b916426ea1d10021f3f17ce8031f93c2.css",
"js/app.js": "build/js/app.13630905267b809161e71d0f8a0c017b.js",
"...": "..."
}
|
In those cases, use the JsonManifestVersionStrategy:
1 2 3 4 5 6 7 8 |
use Symfony\Component\Asset\Package;
use Symfony\Component\Asset\VersionStrategy\JsonManifestVersionStrategy;
// assumes the JSON file above is called "rev-manifest.json"
$package = new Package(new JsonManifestVersionStrategy(__DIR__.'/rev-manifest.json'));
echo $package->getUrl('css/app.css');
// result: build/css/app.b916426ea1d10021f3f17ce8031f93c2.css
|
If you request an asset that is not found in the rev-manifest.json
file,
the original - unmodified - asset path will be returned. The $strictMode
argument helps debug issues because it throws an exception when the asset is not
listed in the manifest:
1 2 3 4 5 6 7 8 9 10 |
use Symfony\Component\Asset\Package;
use Symfony\Component\Asset\VersionStrategy\JsonManifestVersionStrategy;
// The value of $strictMode can be specific per environment "true" for debugging and "false" for stability.
$strictMode = true;
// assumes the JSON file above is called "rev-manifest.json"
$package = new Package(new JsonManifestVersionStrategy(__DIR__.'/rev-manifest.json', null, $strictMode));
echo $package->getUrl('not-found.css');
// error:
|
If your JSON file is not on your local filesystem but is accessible over HTTP, use the RemoteJsonManifestVersionStrategy with the HttpClient component:
1 2 3 4 5 6 7 |
use Symfony\Component\Asset\Package;
use Symfony\Component\Asset\VersionStrategy\RemoteJsonManifestVersionStrategy;
use Symfony\Component\HttpClient\HttpClient;
$httpClient = HttpClient::create();
$manifestUrl = 'https://cdn.example.com/rev-manifest.json';
$package = new Package(new RemoteJsonManifestVersionStrategy($manifestUrl, $httpClient));
|
Custom Version Strategies ¶
Use the VersionStrategyInterface to define your own versioning strategy. For example, your application may need to append the current date to all its web assets in order to bust the cache every day:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
use Symfony\Component\Asset\VersionStrategy\VersionStrategyInterface;
class DateVersionStrategy implements VersionStrategyInterface
{
private $version;
public function __construct()
{
$this->version = date('Ymd');
}
public function getVersion(string $path)
{
return $this->version;
}
public function applyVersion(string $path)
{
return sprintf('%s?v=%s', $path, $this->getVersion($path));
}
}
|
Grouped Assets ¶
Often, many assets live under a common path (e.g. /static/images
). If
that's your case, replace the default Package
class with PathPackage to avoid repeating
that path over and over again:
1 2 3 4 5 6 7 8 9 10 11 |
use Symfony\Component\Asset\PathPackage;
// ...
$pathPackage = new PathPackage('/static/images', new StaticVersionStrategy('v1'));
echo $pathPackage->getUrl('logo.png');
// result: /static/images/logo.png?v1
// Base path is ignored when using absolute paths
echo $pathPackage->getUrl('/logo.png');
// result: /logo.png?v1
|
Request Context Aware Assets ¶
If you are also using the HttpFoundation
component in your project (for instance, in a Symfony application), the PathPackage
class can take into account the context of the current request:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
use Symfony\Component\Asset\Context\RequestStackContext;
use Symfony\Component\Asset\PathPackage;
// ...
$pathPackage = new PathPackage(
'/static/images',
new StaticVersionStrategy('v1'),
new RequestStackContext($requestStack)
);
echo $pathPackage->getUrl('logo.png');
// result: /somewhere/static/images/logo.png?v1
// Both "base path" and "base url" are ignored when using absolute path for asset
echo $pathPackage->getUrl('/logo.png');
// result: /logo.png?v1
|
Now that the request context is set, the PathPackage
will prepend the
current request base URL. So, for example, if your entire site is hosted under
the /somewhere
directory of your web server root directory and the configured
base 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 (Content Delivery Networks) should use the UrlPackage class to generate absolute URLs for their assets:
1 2 3 4 5 6 7 8 9 10 |
use Symfony\Component\Asset\UrlPackage;
// ...
$urlPackage = new UrlPackage(
'http://static.example.com/images/',
new StaticVersionStrategy('v1')
);
echo $urlPackage->getUrl('/logo.png');
// result: http://static.example.com/images/logo.png?v1
|
You can also pass a schema-agnostic URL:
1 2 3 4 5 6 7 8 9 10 |
use Symfony\Component\Asset\UrlPackage;
// ...
$urlPackage = new UrlPackage(
'//static.example.com/images/',
new StaticVersionStrategy('v1')
);
echo $urlPackage->getUrl('/logo.png');
// result: //static.example.com/images/logo.png?v1
|
This is useful because assets will automatically be requested via HTTPS if a visitor is viewing your site in https. If you want to use this, make sure that your CDN host supports HTTPS.
In case you serve assets from more than one domain to improve application
performance, pass an array of URLs as the first argument to the UrlPackage
constructor:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
use Symfony\Component\Asset\UrlPackage;
// ...
$urls = [
'//static1.example.com/images/',
'//static2.example.com/images/',
];
$urlPackage = new UrlPackage($urls, new StaticVersionStrategy('v1'));
echo $urlPackage->getUrl('/logo.png');
// result: http://static1.example.com/images/logo.png?v1
echo $urlPackage->getUrl('/icon.png');
// result: http://static2.example.com/images/icon.png?v1
|
For each asset, one of the URLs will be randomly used. But, the selection is deterministic, meaning that each asset will always be served by the same domain. This behavior simplifies the management of HTTP cache.
Request Context Aware Assets ¶
Similarly to application-relative assets, absolute assets can also take into account the context of the current request. In this case, only the request scheme is considered, in order to select the appropriate base URL (HTTPs or protocol-relative URLs for HTTPs requests, any base URL for HTTP requests):
1 2 3 4 5 6 7 8 9 10 11 12 13 |
use Symfony\Component\Asset\Context\RequestStackContext;
use Symfony\Component\Asset\UrlPackage;
// ...
$urlPackage = new UrlPackage(
['http://example.com/', 'https://example.com/'],
new StaticVersionStrategy('v1'),
new RequestStackContext($requestStack)
);
echo $urlPackage->getUrl('/logo.png');
// assuming the RequestStackContext says that we are on a secure host
// result: https://example.com/logo.png?v1
|
Named Packages ¶
Applications that manage lots of different assets may need to group them in packages with the same versioning strategy and base path. The Asset component includes a Packages class to simplify management of several packages.
In the following example, all packages use the same versioning strategy, but they all have different base paths:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
use Symfony\Component\Asset\Package;
use Symfony\Component\Asset\Packages;
use Symfony\Component\Asset\PathPackage;
use Symfony\Component\Asset\UrlPackage;
// ...
$versionStrategy = new StaticVersionStrategy('v1');
$defaultPackage = new Package($versionStrategy);
$namedPackages = [
'img' => new UrlPackage('http://img.example.com/', $versionStrategy),
'doc' => new PathPackage('/somewhere/deep/for/documents', $versionStrategy),
];
$packages = new Packages($defaultPackage, $namedPackages);
|
The Packages
class allows to define a default package, which will be applied
to assets that don't define the name of the package to use. In addition, this
application defines a package named img
to serve images from an external
domain and a doc
package to avoid repeating long paths when linking to a
document inside a template:
1 2 3 4 5 6 7 8 |
echo $packages->getUrl('/main.css');
// result: /main.css?v1
echo $packages->getUrl('/logo.png', 'img');
// result: http://img.example.com/logo.png?v1
echo $packages->getUrl('resume.pdf', 'doc');
// 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 to
improve performance:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
use Symfony\Component\Asset\UrlPackage;
// ...
$localPackage = new UrlPackage(
'file:///path/to/images/',
new EmptyVersionStrategy()
);
$ftpPackage = new UrlPackage(
'ftp://example.com/images/',
new EmptyVersionStrategy()
);
echo $localPackage->getUrl('/logo.png');
// result: file:///path/to/images/logo.png
echo $ftpPackage->getUrl('/logo.png');
// result: ftp://example.com/images/logo.png
|
Learn more ¶
- How to manage CSS and JavaScript assets in Symfony applicationsSymfony アプリケーションで CSS および JavaScript アセットを管理する方法
- WebLink component to preload assets using HTTP/2.HTTP/2 を使用してアセットをプリロードする WebLink コンポーネント。