LICENSE 0000666 00000002054 13436750441 0005565 0 ustar 00 Copyright (c) 2010-2015 OpenSky Project Inc
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is furnished
to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
CHANGELOG-1.2.md 0000666 00000003573 13436750441 0006676 0 ustar 00 1.2.0 (2014-10-14)
------------------
### New features
* Added the autoprefixer filter
* Added --no-header option for Coffeescript
* Implemented the extraction of dependencies for the compass filter
* Allow custom functions to be registered on the Lessphp filter
* Added MinifyCssCompressor filter based on `mrclay/minify`
* Added `setVariables` in the ScssPhpFilter
* Improved the support of the compress options for UglifyJS2
* Added CssCacheBustingFilter to apply cache busting on URLs in the CSS
* Added support for `--relative-assets` option for the compass filter
### Bug fixes
* Allow functions.php to be included many times
* Updated the ScssPhpFilter for upstream class renaming
1.2.0-alpha1 (2014-07-08)
-------------------------
### BC breaks
* Added `AssetFactory` instance as second argument for `WorkerInterface::process()`
* Removed `LazyAssetManager` from `CacheBustingWorker` constructor
* A new `getSourceDirectory()` method was added on the AssetInterface
* Removed limit and count arguments from CssUtils functions
* Removed the deprecated `PathUtils` class
### New features
* added `CssUtils::filterCommentless()`
* Added `DependencyExtractorInterface` for filters to report other files they import
* Added the support of nib in the stylus filter
* Added `registerFunction` and `setFormatter` to the scssphp filter
* Added the support of flag file for the ClosureCompilerJarFilter
* Added the JsSqueeze filter
* Added support of the define option for uglifyjs (1 & 2) filters
* Added logging for Twig errors in the extractor
### Bug fixes
* Fixed the detection of protocol-relative CSS imports
* Updated AssetCollection to not add several time the same variable in path
* Fixed the merging of the environment variables to keep the configuration of the NODE_PATH working
* Fixed the support of ``{% embed %}`` in the Twig extractor
* Fixed the support of asset variables in GlobAsset
README.md 0000666 00000024261 13436750441 0006043 0 ustar 00 # Assetic [![Build Status](https://travis-ci.org/kriswallsmith/assetic.png?branch=master)](https://travis-ci.org/kriswallsmith/assetic) ![project status](http://stillmaintained.com/kriswallsmith/assetic.png) #
Assetic is an asset management framework for PHP.
``` php
dump();
```
Assets
------
An Assetic asset is something with filterable content that can be loaded and
dumped. An asset also includes metadata, some of which can be manipulated and
some of which is immutable.
| **Property** | **Accessor** | **Mutator** |
|--------------|-----------------|---------------|
| content | getContent | setContent |
| mtime | getLastModified | n/a |
| source root | getSourceRoot | n/a |
| source path | getSourcePath | n/a |
| target path | getTargetPath | setTargetPath |
The "target path" property denotes where an asset (or an collection of assets) should be dumped.
Filters
-------
Filters can be applied to manipulate assets.
``` php
dump();
```
The filters applied to the collection will cascade to each asset leaf if you
iterate over it.
``` php
dump();
}
```
The core provides the following filters in the `Assetic\Filter` namespace:
* `AutoprefixerFilter`: Parse and update vendor-specific properties using autoprefixer
* `CoffeeScriptFilter`: compiles CoffeeScript into Javascript
* `CompassFilter`: Compass CSS authoring framework
* `CssEmbedFilter`: embeds image data in your stylesheets
* `CssImportFilter`: inlines imported stylesheets
* `CssMinFilter`: minifies CSS
* `CleanCssFilter`: minifies CSS
* `CssRewriteFilter`: fixes relative URLs in CSS assets when moving to a new URL
* `DartFilter`: compiles Javascript using dart2js
* `EmberPrecompileFilter`: precompiles Handlebars templates into Javascript for use in the Ember.js framework
* `GoogleClosure\CompilerApiFilter`: compiles Javascript using the Google Closure Compiler API
* `GoogleClosure\CompilerJarFilter`: compiles Javascript using the Google Closure Compiler JAR
* `GssFilter`: compliles CSS using the Google Closure Stylesheets Compiler
* `HandlebarsFilter`: compiles Handlebars templates into Javascript
* `JpegoptimFilter`: optimize your JPEGs
* `JpegtranFilter`: optimize your JPEGs
* `JSMinFilter`: minifies Javascript
* `JSMinPlusFilter`: minifies Javascript
* `JSqueezeFilter`: compresses Javascript
* `LessFilter`: parses LESS into CSS (using less.js with node.js)
* `LessphpFilter`: parses LESS into CSS (using lessphp)
* `OptiPngFilter`: optimize your PNGs
* `PackagerFilter`: parses Javascript for packager tags
* `PackerFilter`: compresses Javascript using Dean Edwards's Packer
* `PhpCssEmbedFilter`: embeds image data in your stylesheet
* `PngoutFilter`: optimize your PNGs
* `ReactJsxFilter`: compiles React JSX into JavaScript
* `Sass\SassFilter`: parses SASS into CSS
* `Sass\ScssFilter`: parses SCSS into CSS
* `ScssphpFilter`: parses SCSS using scssphp
* `SeparatorFilter`: inserts a separator between assets to prevent merge failures
* `SprocketsFilter`: Sprockets Javascript dependency management
* `StylusFilter`: parses STYL into CSS
* `TypeScriptFilter`: parses TypeScript into Javascript
* `UglifyCssFilter`: minifies CSS
* `UglifyJs2Filter`: minifies Javascript
* `UglifyJsFilter`: minifies Javascript
* `Yui\CssCompressorFilter`: compresses CSS using the YUI compressor
* `Yui\JsCompressorFilter`: compresses Javascript using the YUI compressor
Asset Manager
-------------
An asset manager is provided for organizing assets.
``` php
set('jquery', new FileAsset('/path/to/jquery.js'));
$am->set('base_css', new GlobAsset('/path/to/css/*'));
```
The asset manager can also be used to reference assets to avoid duplication.
``` php
set('my_plugin', new AssetCollection(array(
new AssetReference($am, 'jquery'),
new FileAsset('/path/to/jquery.plugin.js'),
)));
```
Filter Manager
--------------
A filter manager is also provided for organizing filters.
``` php
set('sass', new SassFilter('/path/to/parser/sass'));
$fm->set('yui_css', new Yui\CssCompressorFilter('/path/to/yuicompressor.jar'));
```
Asset Factory
-------------
If you'd rather not create all these objects by hand, you can use the asset
factory, which will do most of the work for you.
``` php
setAssetManager($am);
$factory->setFilterManager($fm);
$factory->setDebug(true);
$css = $factory->createAsset(array(
'@reset', // load the asset manager's "reset" asset
'css/src/*.scss', // load every scss files from "/path/to/asset/directory/css/src/"
), array(
'scss', // filter through the filter manager's "scss" filter
'?yui_css', // don't use this filter in debug mode
));
echo $css->dump();
```
The `AssetFactory` is constructed with a root directory which is used as the base directory for relative asset paths.
Prefixing a filter name with a question mark, as `yui_css` is here, will cause
that filter to be omitted when the factory is in debug mode.
You can also register [Workers](src/Assetic/Factory/Worker/WorkerInterface.php) on the factory and all assets created
by it will be passed to the worker's `process()` method before being returned. See _Cache Busting_ below for an example.
Dumping Assets to static files
------------------------------
You can dump all the assets an AssetManager holds to files in a directory. This will probably be below your webserver's document root
so the files can be served statically.
``` php
writeManagerAssets($am);
```
This will make use of the assets' target path.
Cache Busting
-------------
If you serve your assets from static files as just described, you can use the CacheBustingWorker to rewrite the target
paths for assets. It will insert an identifier before the filename extension that is unique for a particular version
of the asset.
This identifier is based on the modification time of the asset and will also take depended-on assets into
consideration if the applied filters support it.
``` php
setAssetManager($am);
$factory->setFilterManager($fm);
$factory->setDebug(true);
$factory->addWorker(new CacheBustingWorker());
$css = $factory->createAsset(array(
'@reset', // load the asset manager's "reset" asset
'css/src/*.scss', // load every scss files from "/path/to/asset/directory/css/src/"
), array(
'scss', // filter through the filter manager's "scss" filter
'?yui_css', // don't use this filter in debug mode
));
echo $css->dump();
```
Internal caching
-------
A simple caching mechanism is provided to avoid unnecessary work.
``` php
dump();
$js->dump();
$js->dump();
```
Twig
----
To use the Assetic [Twig][3] extension you must register it to your Twig
environment:
``` php
addExtension(new AsseticExtension($factory));
```
Once in place, the extension exposes a stylesheets and a javascripts tag with a syntax similar
to what the asset factory uses:
``` html+jinja
{% stylesheets '/path/to/sass/main.sass' filter='sass,?yui_css' output='css/all.css' %}
{% endstylesheets %}
```
This example will render one `link` element on the page that includes a URL
where the filtered asset can be found.
When the extension is in debug mode, this same tag will render multiple `link`
elements, one for each asset referenced by the `css/src/*.sass` glob. The
specified filters will still be applied, unless they are marked as optional
using the `?` prefix.
This behavior can also be triggered by setting a `debug` attribute on the tag:
``` html+jinja
{% stylesheets 'css/*' debug=true %} ... {% stylesheets %}
```
These assets need to be written to the web directory so these URLs don't
return 404 errors.
``` php
setLoader('twig', new TwigFormulaLoader($twig));
// loop through all your templates
foreach ($templates as $template) {
$resource = new TwigResource($twigLoader, $template);
$am->addResource($resource, 'twig');
}
$writer = new AssetWriter('/path/to/web');
$writer->writeManagerAssets($am);
```
---
Assetic is based on the Python [webassets][1] library (available on
[GitHub][2]).
[1]: http://elsdoerfer.name/docs/webassets
[2]: https://github.com/miracle2k/webassets
[3]: http://twig.sensiolabs.org
package.json 0000666 00000000567 13436750441 0007055 0 ustar 00 {
"devDependencies": {
"uglifycss": "*",
"coffee-script": "*",
"stylus": "*",
"nib": "*",
"ember-precompile": "*",
"typescript": "*",
"less": "*",
"handlebars": "*",
"uglify-js": "*",
"autoprefixer": "*",
"autoprefixer-5": "^1.x",
"autoprefixer-cli": "*",
"roole": "*",
"react-tools": "*",
"clean-css": "*"
}
}
CHANGELOG-1.0.md 0000666 00000001610 13436750441 0006662 0 ustar 00 1.0.4 (August 28, 2012)
-----------------------
* Fixed the Twig tag to avoid a fatal error when left unclosed
* Added the HashableInterface for non-serialiable filters
* Fixed a bug for compass on windows
1.0.3 (March 2, 2012)
---------------------
* Added "boring" option to Compass filter
* Fixed accumulation of load paths in Compass filter
* Fixed issues in CssImport and CssRewrite filters
1.0.2 (August 26, 2011)
-----------------------
* Twig 1.2 compatibility
* Fixed filtering of large LessCSS assets
* Fixed escaping of commands on Windows
* Misc fixes to Compass filter
* Removed default CssEmbed charset
1.0.1 (July 15, 2011)
---------------------
* Fixed Twig error handling
* Removed use of STDIN
* Added inheritance of environment variables
* Fixed Compass on Windows
* Improved escaping of commands
1.0.0 (July 10, 2011)
---------------------
* Initial release
Gemfile 0000666 00000000124 13436750441 0006047 0 ustar 00 source "https://rubygems.org"
gem "sprockets", "~> 1.0.0"
gem "sass"
gem "compass"
CHANGELOG-1.1.md 0000666 00000004371 13436750441 0006672 0 ustar 00 1.1.2 (July 18, 2013)
-------------------
* Fixed deep mtime on asset collections
* `CallablesFilter` now implements `DependencyExtractorInterface`
* Fixed detection of "partial" children in subfolders in `SassFilter`
* Restored `PathUtils` for BC
1.1.1 (June 1, 2013)
--------------------
* Fixed cloning of asset collections
* Fixed environment var inheritance
* Replaced `AssetWriter::getCombinations()` for BC, even though we don't use it
* Added support for `@import-once` to Less filters
1.1.0 (May 15, 2013)
--------------------
* Added LazyAssetManager::getLastModified() for determining "deep" mtime
* Added DartFilter
* Added EmberPrecompile
* Added GssFilter
* Added PhpCssEmbedFilter
* Added RooleFilter
* Added TypeScriptFilter
* Added the possibility to configure additional load paths for less and lessphp
* Added the UglifyCssFilter
* Fixed the handling of directories in the GlobAsset. #256
* Added Handlebars support
* Added Scssphp-compass support
* Added the CacheBustingWorker
* Added the UglifyJs2Filter
1.1.0-alpha1 (August 28, 2012)
------------------------------
* Added pure php css embed filter
* Added Scssphp support
* Added support for Google Closure language option
* Added a way to set a specific ruby path for CompassFilter and SassFilter
* Ensure uniqueness of temporary files created by the compressor filter. Fixed #61
* Added Compass option for generated_images_path (for generated Images/Sprites)
* Added PackerFilter
* Add the way to contact closure compiler API using curl, if available and allow_url_fopen is off
* Added filters for JSMin and JSMinPlus
* Added the UglifyJsFilter
* Improved the error message in getModifiedTime when a file asset uses an invalid file
* added support for asset variables:
Asset variables allow you to pre-compile your assets for a finite set of known
variable values, and then to simply deliver the correct asset version at runtime.
For example, this is helpful for assets with language, or browser-specific code.
* Removed the copy-paste of the Symfony2 Process component and use the original one
* Added ability to pass variables into lessphp filter
* Added google closure stylesheets jar filter
* Added the support of `--bare` for the CoffeeScriptFilter
src/functions.php 0000666 00000006054 13436750441 0010074 0 ustar 00 factory = $factory;
}
/**
* Returns an array of javascript URLs.
*
* @param array|string $inputs Input strings
* @param array|string $filters Filter names
* @param array $options An array of options
*
* @return array An array of javascript URLs
*/
function assetic_javascripts($inputs = array(), $filters = array(), array $options = array())
{
if (!isset($options['output'])) {
$options['output'] = 'js/*.js';
}
return _assetic_urls($inputs, $filters, $options);
}
/**
* Returns an array of stylesheet URLs.
*
* @param array|string $inputs Input strings
* @param array|string $filters Filter names
* @param array $options An array of options
*
* @return array An array of stylesheet URLs
*/
function assetic_stylesheets($inputs = array(), $filters = array(), array $options = array())
{
if (!isset($options['output'])) {
$options['output'] = 'css/*.css';
}
return _assetic_urls($inputs, $filters, $options);
}
/**
* Returns an image URL.
*
* @param string $input An input
* @param array|string $filters Filter names
* @param array $options An array of options
*
* @return string An image URL
*/
function assetic_image($input, $filters = array(), array $options = array())
{
if (!isset($options['output'])) {
$options['output'] = 'images/*';
}
$urls = _assetic_urls($input, $filters, $options);
return current($urls);
}
/**
* Returns an array of asset urls.
*
* @param array|string $inputs Input strings
* @param array|string $filters Filter names
* @param array $options An array of options
*
* @return array An array of URLs
*/
function _assetic_urls($inputs = array(), $filters = array(), array $options = array())
{
global $_assetic;
if (!is_array($inputs)) {
$inputs = array_filter(array_map('trim', explode(',', $inputs)));
}
if (!is_array($filters)) {
$filters = array_filter(array_map('trim', explode(',', $filters)));
}
$coll = $_assetic->factory->createAsset($inputs, $filters, $options);
$debug = isset($options['debug']) ? $options['debug'] : $_assetic->factory->isDebug();
$combine = isset($options['combine']) ? $options['combine'] : !$debug;
$one = $coll->getTargetPath();
if ($combine) {
$many = array($one);
} else {
$many = array();
foreach ($coll as $leaf) {
$many[] = $leaf->getTargetPath();
}
}
return new TraversableString($one, $many);
}
src/Assetic/Cache/ConfigCache.php 0000666 00000006044 13436750441 0012612 0 ustar 00
*/
class ConfigCache
{
private $dir;
/**
* Construct.
*
* @param string $dir The cache directory
*/
public function __construct($dir)
{
$this->dir = $dir;
}
/**
* Checks of the cache has a file.
*
* @param string $resource A cache key
*
* @return Boolean True if a file exists
*/
public function has($resource)
{
return file_exists($this->getSourcePath($resource));
}
/**
* Writes a value to a file.
*
* @param string $resource A cache key
* @param mixed $value A value to cache
*/
public function set($resource, $value)
{
$path = $this->getSourcePath($resource);
if (!is_dir($dir = dirname($path)) && false === @mkdir($dir, 0777, true)) {
// @codeCoverageIgnoreStart
throw new \RuntimeException('Unable to create directory '.$dir);
// @codeCoverageIgnoreEnd
}
if (false === @file_put_contents($path, sprintf("getSourcePath($resource);
if (!file_exists($path)) {
throw new \RuntimeException('There is no cached value for '.$resource);
}
return include $path;
}
/**
* Returns a timestamp for when the cache was created.
*
* @param string $resource A cache key
*
* @return integer A UNIX timestamp
*/
public function getTimestamp($resource)
{
$path = $this->getSourcePath($resource);
if (!file_exists($path)) {
throw new \RuntimeException('There is no cached value for '.$resource);
}
if (false === $mtime = @filemtime($path)) {
// @codeCoverageIgnoreStart
throw new \RuntimeException('Unable to determine file mtime for '.$path);
// @codeCoverageIgnoreEnd
}
return $mtime;
}
/**
* Returns the path where the file corresponding to the supplied cache key can be included from.
*
* @param string $resource A cache key
*
* @return string A file path
*/
private function getSourcePath($resource)
{
$key = md5($resource);
return $this->dir.'/'.$key[0].'/'.$key.'.php';
}
}
src/Assetic/Cache/ApcCache.php 0000666 00000002374 13436750441 0012112 0 ustar 00
*/
class ApcCache implements CacheInterface
{
public $ttl = 0;
/**
* @see CacheInterface::has()
*/
public function has($key)
{
return apc_exists($key);
}
/**
* @see CacheInterface::get()
*/
public function get($key)
{
$value = apc_fetch($key, $success);
if (!$success) {
throw new \RuntimeException('There is no cached value for '.$key);
}
return $value;
}
/**
* @see CacheInterface::set()
*/
public function set($key, $value)
{
$store = apc_store($key, $value, $this->ttl);
if (!$store) {
throw new \RuntimeException('Unable to store "'.$key.'" for '.$this->ttl.' seconds.');
}
return $store;
}
/**
* @see CacheInterface::remove()
*/
public function remove($key)
{
return apc_delete($key);
}
}
src/Assetic/Cache/CacheInterface.php 0000666 00000002147 13436750441 0013305 0 ustar 00
*/
interface CacheInterface
{
/**
* Checks if the cache has a value for a key.
*
* @param string $key A unique key
*
* @return Boolean Whether the cache has a value for this key
*/
public function has($key);
/**
* Returns the value for a key.
*
* @param string $key A unique key
*
* @return string|null The value in the cache
*/
public function get($key);
/**
* Sets a value in the cache.
*
* @param string $key A unique key
* @param string $value The value to cache
*/
public function set($key, $value);
/**
* Removes a value from the cache.
*
* @param string $key A unique key
*/
public function remove($key);
}
src/Assetic/Cache/ExpiringCache.php 0000666 00000002432 13436750441 0013167 0 ustar 00
*/
class ExpiringCache implements CacheInterface
{
private $cache;
private $lifetime;
public function __construct(CacheInterface $cache, $lifetime)
{
$this->cache = $cache;
$this->lifetime = $lifetime;
}
public function has($key)
{
if ($this->cache->has($key)) {
if (time() < $this->cache->get($key.'.expires')) {
return true;
}
$this->cache->remove($key.'.expires');
$this->cache->remove($key);
}
return false;
}
public function get($key)
{
return $this->cache->get($key);
}
public function set($key, $value)
{
$this->cache->set($key.'.expires', time() + $this->lifetime);
$this->cache->set($key, $value);
}
public function remove($key)
{
$this->cache->remove($key.'.expires');
$this->cache->remove($key);
}
}
src/Assetic/Cache/ArrayCache.php 0000666 00000002103 13436750441 0012453 0 ustar 00
*/
class ArrayCache implements CacheInterface
{
private $cache = array();
/**
* @see CacheInterface::has()
*/
public function has($key)
{
return isset($this->cache[$key]);
}
/**
* @see CacheInterface::get()
*/
public function get($key)
{
if (!$this->has($key)) {
throw new \RuntimeException('There is no cached value for '.$key);
}
return $this->cache[$key];
}
/**
* @see CacheInterface::set()
*/
public function set($key, $value)
{
$this->cache[$key] = $value;
}
/**
* @see CacheInterface::remove()
*/
public function remove($key)
{
unset($this->cache[$key]);
}
}
src/Assetic/Cache/FilesystemCache.php 0000666 00000002721 13436750441 0013527 0 ustar 00
*/
class FilesystemCache implements CacheInterface
{
private $dir;
public function __construct($dir)
{
$this->dir = $dir;
}
public function has($key)
{
return file_exists($this->dir.'/'.$key);
}
public function get($key)
{
$path = $this->dir.'/'.$key;
if (!file_exists($path)) {
throw new \RuntimeException('There is no cached value for '.$key);
}
return file_get_contents($path);
}
public function set($key, $value)
{
if (!is_dir($this->dir) && false === @mkdir($this->dir, 0777, true)) {
throw new \RuntimeException('Unable to create directory '.$this->dir);
}
$path = $this->dir.'/'.$key;
if (false === @file_put_contents($path, $value)) {
throw new \RuntimeException('Unable to write file '.$path);
}
}
public function remove($key)
{
$path = $this->dir.'/'.$key;
if (file_exists($path) && false === @unlink($path)) {
throw new \RuntimeException('Unable to remove file '.$path);
}
}
}
src/Assetic/Filter/HashableInterface.php 0000666 00000001027 13436750441 0014227 0 ustar 00
*/
interface HashableInterface
{
/**
* Generates a hash for the object
*
* @return string Object hash
*/
public function hash();
}
src/Assetic/Filter/BaseCssFilter.php 0000666 00000002617 13436750441 0013376 0 ustar 00
*/
abstract class BaseCssFilter implements FilterInterface
{
/**
* @see CssUtils::filterReferences()
*/
protected function filterReferences($content, $callback, $limit = -1, &$count = 0)
{
return CssUtils::filterReferences($content, $callback, $limit, $count);
}
/**
* @see CssUtils::filterUrls()
*/
protected function filterUrls($content, $callback, $limit = -1, &$count = 0)
{
return CssUtils::filterUrls($content, $callback, $limit, $count);
}
/**
* @see CssUtils::filterImports()
*/
protected function filterImports($content, $callback, $limit = -1, &$count = 0, $includeUrl = true)
{
return CssUtils::filterImports($content, $callback, $limit, $count, $includeUrl);
}
/**
* @see CssUtils::filterIEFilters()
*/
protected function filterIEFilters($content, $callback, $limit = -1, &$count = 0)
{
return CssUtils::filterIEFilters($content, $callback, $limit, $count);
}
}
src/Assetic/Filter/StylusFilter.php 0000666 00000005646 13436750441 0013363 0 ustar 00
*/
class StylusFilter extends BaseNodeFilter implements DependencyExtractorInterface
{
private $nodeBin;
private $compress;
private $useNib;
/**
* Constructs filter.
*
* @param string $nodeBin The path to the node binary
* @param array $nodePaths An array of node paths
*/
public function __construct($nodeBin = '/usr/bin/node', array $nodePaths = array())
{
$this->nodeBin = $nodeBin;
$this->setNodePaths($nodePaths);
}
/**
* Enable output compression.
*
* @param boolean $compress
*/
public function setCompress($compress)
{
$this->compress = $compress;
}
/**
* Enable the use of Nib
*
* @param boolean $useNib
*/
public function setUseNib($useNib)
{
$this->useNib = $useNib;
}
/**
* {@inheritdoc}
*/
public function filterLoad(AssetInterface $asset)
{
static $format = <<<'EOF'
var stylus = require('stylus');
var sys = require(process.binding('natives').util ? 'util' : 'sys');
stylus(%s, %s)%s.render(function(e, css){
if (e) {
throw e;
}
sys.print(css);
process.exit(0);
});
EOF;
// parser options
$parserOptions = array();
if ($dir = $asset->getSourceDirectory()) {
$parserOptions['paths'] = array($dir);
$parserOptions['filename'] = basename($asset->getSourcePath());
}
if (null !== $this->compress) {
$parserOptions['compress'] = $this->compress;
}
$pb = $this->createProcessBuilder();
$pb->add($this->nodeBin)->add($input = FilesystemUtils::createTemporaryFile('stylus'));
file_put_contents($input, sprintf($format,
json_encode($asset->getContent()),
json_encode($parserOptions),
$this->useNib ? '.use(require(\'nib\')())' : ''
));
$proc = $pb->getProcess();
$code = $proc->run();
unlink($input);
if (0 !== $code) {
throw FilterException::fromProcess($proc)->setInput($asset->getContent());
}
$asset->setContent($proc->getOutput());
}
/**
* {@inheritdoc}
*/
public function filterDump(AssetInterface $asset)
{
}
public function getChildren(AssetFactory $factory, $content, $loadPath = null)
{
// todo
return array();
}
}
src/Assetic/Filter/TypeScriptFilter.php 0000666 00000004211 13436750441 0014151 0 ustar 00
*/
class TypeScriptFilter extends BaseNodeFilter
{
private $tscBin;
private $nodeBin;
public function __construct($tscBin = '/usr/bin/tsc', $nodeBin = null)
{
$this->tscBin = $tscBin;
$this->nodeBin = $nodeBin;
}
public function filterLoad(AssetInterface $asset)
{
$pb = $this->createProcessBuilder($this->nodeBin
? array($this->nodeBin, $this->tscBin)
: array($this->tscBin));
if ($sourcePath = $asset->getSourcePath()) {
$templateName = basename($sourcePath);
} else {
$templateName = 'asset';
}
$inputDirPath = FilesystemUtils::createThrowAwayDirectory('typescript_in');
$inputPath = $inputDirPath.DIRECTORY_SEPARATOR.$templateName.'.ts';
$outputPath = FilesystemUtils::createTemporaryFile('typescript_out');
file_put_contents($inputPath, $asset->getContent());
$pb->add($inputPath)->add('--out')->add($outputPath);
$proc = $pb->getProcess();
$code = $proc->run();
unlink($inputPath);
rmdir($inputDirPath);
if (0 !== $code) {
if (file_exists($outputPath)) {
unlink($outputPath);
}
throw FilterException::fromProcess($proc)->setInput($asset->getContent());
}
if (!file_exists($outputPath)) {
throw new \RuntimeException('Error creating output file.');
}
$compiledJs = file_get_contents($outputPath);
unlink($outputPath);
$asset->setContent($compiledJs);
}
public function filterDump(AssetInterface $asset)
{
}
}
src/Assetic/Filter/UglifyJs2Filter.php 0000666 00000007004 13436750441 0013664 0 ustar 00
*/
class UglifyJs2Filter extends BaseNodeFilter
{
private $uglifyjsBin;
private $nodeBin;
private $compress;
private $beautify;
private $mangle;
private $screwIe8;
private $comments;
private $wrap;
private $defines;
public function __construct($uglifyjsBin = '/usr/bin/uglifyjs', $nodeBin = null)
{
$this->uglifyjsBin = $uglifyjsBin;
$this->nodeBin = $nodeBin;
}
public function setCompress($compress)
{
$this->compress = $compress;
}
public function setBeautify($beautify)
{
$this->beautify = $beautify;
}
public function setMangle($mangle)
{
$this->mangle = $mangle;
}
public function setScrewIe8($screwIe8)
{
$this->screwIe8 = $screwIe8;
}
public function setComments($comments)
{
$this->comments = $comments;
}
public function setWrap($wrap)
{
$this->wrap = $wrap;
}
public function setDefines(array $defines)
{
$this->defines = $defines;
}
public function filterLoad(AssetInterface $asset)
{
}
public function filterDump(AssetInterface $asset)
{
$pb = $this->createProcessBuilder(
$this->nodeBin
? array($this->nodeBin, $this->uglifyjsBin)
: array($this->uglifyjsBin)
);
if ($this->compress) {
$pb->add('--compress');
if (is_string($this->compress) && !empty($this->compress)) {
$pb->add($this->compress);
}
}
if ($this->beautify) {
$pb->add('--beautify');
}
if ($this->mangle) {
$pb->add('--mangle');
}
if ($this->screwIe8) {
$pb->add('--screw-ie8');
}
if ($this->comments) {
$pb->add('--comments')->add(true === $this->comments ? 'all' : $this->comments);
}
if ($this->wrap) {
$pb->add('--wrap')->add($this->wrap);
}
if ($this->defines) {
$pb->add('--define')->add(implode(',', $this->defines));
}
// input and output files
$input = FilesystemUtils::createTemporaryFile('uglifyjs2_in');
$output = FilesystemUtils::createTemporaryFile('uglifyjs2_out');
file_put_contents($input, $asset->getContent());
$pb->add('-o')->add($output)->add($input);
$proc = $pb->getProcess();
$code = $proc->run();
unlink($input);
if (0 !== $code) {
if (file_exists($output)) {
unlink($output);
}
if (127 === $code) {
throw new \RuntimeException('Path to node executable could not be resolved.');
}
throw FilterException::fromProcess($proc)->setInput($asset->getContent());
}
if (!file_exists($output)) {
throw new \RuntimeException('Error creating output file.');
}
$asset->setContent(file_get_contents($output));
unlink($output);
}
}
src/Assetic/Filter/ScssphpFilter.php 0000666 00000007471 13436750441 0013501 0 ustar 00
*/
class ScssphpFilter implements DependencyExtractorInterface
{
private $compass = false;
private $importPaths = array();
private $customFunctions = array();
private $formatter;
private $variables = array();
public function enableCompass($enable = true)
{
$this->compass = (Boolean) $enable;
}
public function isCompassEnabled()
{
return $this->compass;
}
public function setFormatter($formatter)
{
$legacyFormatters = array(
'scss_formatter' => 'Leafo\ScssPhp\Formatter\Expanded',
'scss_formatter_nested' => 'Leafo\ScssPhp\Formatter\Nested',
'scss_formatter_compressed' => 'Leafo\ScssPhp\Formatter\Compressed',
'scss_formatter_crunched' => 'Leafo\ScssPhp\Formatter\Crunched',
);
if (isset($legacyFormatters[$formatter])) {
@trigger_error(sprintf('The scssphp formatter `%s` is deprecated. Use `%s` instead.', $formatter, $legacyFormatters[$formatter]), E_USER_DEPRECATED);
$formatter = $legacyFormatters[$formatter];
}
$this->formatter = $formatter;
}
public function setVariables(array $variables)
{
$this->variables = $variables;
}
public function addVariable($variable)
{
$this->variables[] = $variable;
}
public function setImportPaths(array $paths)
{
$this->importPaths = $paths;
}
public function addImportPath($path)
{
$this->importPaths[] = $path;
}
public function registerFunction($name, $callable)
{
$this->customFunctions[$name] = $callable;
}
public function filterLoad(AssetInterface $asset)
{
$sc = new Compiler();
if ($this->compass) {
new \scss_compass($sc);
}
if ($dir = $asset->getSourceDirectory()) {
$sc->addImportPath($dir);
}
foreach ($this->importPaths as $path) {
$sc->addImportPath($path);
}
foreach ($this->customFunctions as $name => $callable) {
$sc->registerFunction($name, $callable);
}
if ($this->formatter) {
$sc->setFormatter($this->formatter);
}
if (!empty($this->variables)) {
$sc->setVariables($this->variables);
}
$asset->setContent($sc->compile($asset->getContent()));
}
public function filterDump(AssetInterface $asset)
{
}
public function getChildren(AssetFactory $factory, $content, $loadPath = null)
{
$sc = new Compiler();
if ($loadPath !== null) {
$sc->addImportPath($loadPath);
}
foreach ($this->importPaths as $path) {
$sc->addImportPath($path);
}
$children = array();
foreach (CssUtils::extractImports($content) as $match) {
$file = $sc->findImport($match);
if ($file) {
$children[] = $child = $factory->createAsset($file, array(), array('root' => $loadPath));
$child->load();
$children = array_merge($children, $this->getChildren($factory, $child->getContent(), $loadPath));
}
}
return $children;
}
}
src/Assetic/Filter/DartFilter.php 0000666 00000003213 13436750441 0012736 0 ustar 00 dartBin = $dartBin;
}
public function filterLoad(AssetInterface $asset)
{
$input = FilesystemUtils::createTemporaryFile('dart');
$output = FilesystemUtils::createTemporaryFile('dart');
file_put_contents($input, $asset->getContent());
$pb = $this->createProcessBuilder()
->add($this->dartBin)
->add('-o'.$output)
->add($input)
;
$proc = $pb->getProcess();
$code = $proc->run();
unlink($input);
if (0 !== $code) {
$this->cleanup($output);
throw FilterException::fromProcess($proc);
}
if (!file_exists($output)) {
throw new \RuntimeException('Error creating output file.');
}
$asset->setContent(file_get_contents($output));
$this->cleanup($output);
}
public function filterDump(AssetInterface $asset)
{
}
private function cleanup($file)
{
foreach (glob($file.'*') as $related) {
unlink($related);
}
}
}
src/Assetic/Filter/ReactJsxFilter.php 0000666 00000003716 13436750441 0013577 0 ustar 00
*/
class ReactJsxFilter extends BaseNodeFilter
{
private $jsxBin;
private $nodeBin;
public function __construct($jsxBin = '/usr/bin/jsx', $nodeBin = null)
{
$this->jsxBin = $jsxBin;
$this->nodeBin = $nodeBin;
}
public function filterLoad(AssetInterface $asset)
{
$builder = $this->createProcessBuilder($this->nodeBin
? array($this->nodeBin, $this->jsxBin)
: array($this->jsxBin));
$inputDir = FilesystemUtils::createThrowAwayDirectory('jsx_in');
$inputFile = $inputDir.DIRECTORY_SEPARATOR.'asset.js';
$outputDir = FilesystemUtils::createThrowAwayDirectory('jsx_out');
$outputFile = $outputDir.DIRECTORY_SEPARATOR.'asset.js';
// create the asset file
file_put_contents($inputFile, $asset->getContent());
$builder
->add($inputDir)
->add($outputDir)
->add('--no-cache-dir')
;
$proc = $builder->getProcess();
$code = $proc->run();
// remove the input directory and asset file
unlink($inputFile);
rmdir($inputDir);
if (0 !== $code) {
if (file_exists($outputFile)) {
unlink($outputFile);
}
if (file_exists($outputDir)) {
rmdir($outputDir);
}
throw FilterException::fromProcess($proc);
}
$asset->setContent(file_get_contents($outputFile));
// remove the output directory and processed asset file
unlink($outputFile);
rmdir($outputDir);
}
public function filterDump(AssetInterface $asset)
{
}
}
src/Assetic/Filter/UglifyCssFilter.php 0000666 00000005715 13436750441 0013765 0 ustar 00
*/
class UglifyCssFilter extends BaseNodeFilter
{
private $uglifycssBin;
private $nodeBin;
private $expandVars;
private $uglyComments;
private $cuteComments;
/**
* @param string $uglifycssBin Absolute path to the uglifycss executable
* @param string $nodeBin Absolute path to the folder containg node.js executable
*/
public function __construct($uglifycssBin = '/usr/bin/uglifycss', $nodeBin = null)
{
$this->uglifycssBin = $uglifycssBin;
$this->nodeBin = $nodeBin;
}
/**
* Expand variables
* @param bool $expandVars True to enable
*/
public function setExpandVars($expandVars)
{
$this->expandVars = $expandVars;
}
/**
* Remove newlines within preserved comments
* @param bool $uglyComments True to enable
*/
public function setUglyComments($uglyComments)
{
$this->uglyComments = $uglyComments;
}
/**
* Preserve newlines within and around preserved comments
* @param bool $cuteComments True to enable
*/
public function setCuteComments($cuteComments)
{
$this->cuteComments = $cuteComments;
}
/**
* @see Assetic\Filter\FilterInterface::filterLoad()
*/
public function filterLoad(AssetInterface $asset)
{
}
/**
* Run the asset through UglifyJs
*
* @see Assetic\Filter\FilterInterface::filterDump()
*/
public function filterDump(AssetInterface $asset)
{
$pb = $this->createProcessBuilder($this->nodeBin
? array($this->nodeBin, $this->uglifycssBin)
: array($this->uglifycssBin));
if ($this->expandVars) {
$pb->add('--expand-vars');
}
if ($this->uglyComments) {
$pb->add('--ugly-comments');
}
if ($this->cuteComments) {
$pb->add('--cute-comments');
}
// input and output files
$input = FilesystemUtils::createTemporaryFile('uglifycss');
file_put_contents($input, $asset->getContent());
$pb->add($input);
$proc = $pb->getProcess();
$code = $proc->run();
unlink($input);
if (127 === $code) {
throw new \RuntimeException('Path to node executable could not be resolved.');
}
if (0 !== $code) {
throw FilterException::fromProcess($proc)->setInput($asset->getContent());
}
$asset->setContent($proc->getOutput());
}
}
src/Assetic/Filter/Yui/JsCompressorFilter.php 0000666 00000002603 13436750441 0015245 0 ustar 00
*/
class JsCompressorFilter extends BaseCompressorFilter
{
private $nomunge;
private $preserveSemi;
private $disableOptimizations;
public function setNomunge($nomunge = true)
{
$this->nomunge = $nomunge;
}
public function setPreserveSemi($preserveSemi)
{
$this->preserveSemi = $preserveSemi;
}
public function setDisableOptimizations($disableOptimizations)
{
$this->disableOptimizations = $disableOptimizations;
}
public function filterDump(AssetInterface $asset)
{
$options = array();
if ($this->nomunge) {
$options[] = '--nomunge';
}
if ($this->preserveSemi) {
$options[] = '--preserve-semi';
}
if ($this->disableOptimizations) {
$options[] = '--disable-optimizations';
}
$asset->setContent($this->compress($asset->getContent(), 'js', $options));
}
}
src/Assetic/Filter/Yui/CssCompressorFilter.php 0000666 00000001225 13436750441 0015420 0 ustar 00
*/
class CssCompressorFilter extends BaseCompressorFilter
{
public function filterDump(AssetInterface $asset)
{
$asset->setContent($this->compress($asset->getContent(), 'css'));
}
}
src/Assetic/Filter/Yui/BaseCompressorFilter.php 0000666 00000005667 13436750441 0015560 0 ustar 00
*/
abstract class BaseCompressorFilter extends BaseProcessFilter
{
private $jarPath;
private $javaPath;
private $charset;
private $lineBreak;
private $stackSize;
public function __construct($jarPath, $javaPath = '/usr/bin/java')
{
$this->jarPath = $jarPath;
$this->javaPath = $javaPath;
}
public function setCharset($charset)
{
$this->charset = $charset;
}
public function setLineBreak($lineBreak)
{
$this->lineBreak = $lineBreak;
}
public function setStackSize($stackSize)
{
$this->stackSize = $stackSize;
}
public function filterLoad(AssetInterface $asset)
{
}
/**
* Compresses a string.
*
* @param string $content The content to compress
* @param string $type The type of content, either "js" or "css"
* @param array $options An indexed array of additional options
*
* @return string The compressed content
*/
protected function compress($content, $type, $options = array())
{
$pb = $this->createProcessBuilder(array($this->javaPath));
if (null !== $this->stackSize) {
$pb->add('-Xss'.$this->stackSize);
}
$pb->add('-jar')->add($this->jarPath);
foreach ($options as $option) {
$pb->add($option);
}
if (null !== $this->charset) {
$pb->add('--charset')->add($this->charset);
}
if (null !== $this->lineBreak) {
$pb->add('--line-break')->add($this->lineBreak);
}
// input and output files
$tempDir = FilesystemUtils::getTemporaryDirectory();
$input = tempnam($tempDir, 'assetic_yui_input');
$output = tempnam($tempDir, 'assetic_yui_output');
file_put_contents($input, $content);
$pb->add('-o')->add($output)->add('--type')->add($type)->add($input);
$proc = $pb->getProcess();
$code = $proc->run();
unlink($input);
if (0 !== $code) {
if (file_exists($output)) {
unlink($output);
}
throw FilterException::fromProcess($proc)->setInput($content);
}
if (!file_exists($output)) {
throw new \RuntimeException('Error creating output file.');
}
$retval = file_get_contents($output);
unlink($output);
return $retval;
}
}
src/Assetic/Filter/EmberPrecompileFilter.php 0000666 00000005105 13436750441 0015120 0 ustar 00
*/
class EmberPrecompileFilter extends BaseNodeFilter
{
private $emberBin;
private $nodeBin;
public function __construct($handlebarsBin = '/usr/bin/ember-precompile', $nodeBin = null)
{
$this->emberBin = $handlebarsBin;
$this->nodeBin = $nodeBin;
}
public function filterLoad(AssetInterface $asset)
{
$pb = $this->createProcessBuilder($this->nodeBin
? array($this->nodeBin, $this->emberBin)
: array($this->emberBin));
if ($sourcePath = $asset->getSourcePath()) {
$templateName = basename($sourcePath);
} else {
throw new \LogicException('The embed-precompile filter requires that assets have a source path set');
}
$inputDirPath = FilesystemUtils::createThrowAwayDirectory('ember_in');
$inputPath = $inputDirPath.DIRECTORY_SEPARATOR.$templateName;
$outputPath = FilesystemUtils::createTemporaryFile('ember_out');
file_put_contents($inputPath, $asset->getContent());
$pb->add($inputPath)->add('-f')->add($outputPath);
$process = $pb->getProcess();
$returnCode = $process->run();
unlink($inputPath);
rmdir($inputDirPath);
if (127 === $returnCode) {
throw new \RuntimeException('Path to node executable could not be resolved.');
}
if (0 !== $returnCode) {
if (file_exists($outputPath)) {
unlink($outputPath);
}
throw FilterException::fromProcess($process)->setInput($asset->getContent());
}
if (!file_exists($outputPath)) {
throw new \RuntimeException('Error creating output file.');
}
$compiledJs = file_get_contents($outputPath);
unlink($outputPath);
$asset->setContent($compiledJs);
}
public function filterDump(AssetInterface $asset)
{
}
}
src/Assetic/Filter/GssFilter.php 0000666 00000007451 13436750441 0012610 0 ustar 00
*/
class GssFilter extends BaseProcessFilter
{
private $jarPath;
private $javaPath;
private $allowUnrecognizedFunctions;
private $allowedNonStandardFunctions;
private $copyrightNotice;
private $define;
private $gssFunctionMapProvider;
private $inputOrientation;
private $outputOrientation;
private $prettyPrint;
public function __construct($jarPath, $javaPath = '/usr/bin/java')
{
$this->jarPath = $jarPath;
$this->javaPath = $javaPath;
}
public function setAllowUnrecognizedFunctions($allowUnrecognizedFunctions)
{
$this->allowUnrecognizedFunctions = $allowUnrecognizedFunctions;
}
public function setAllowedNonStandardFunctions($allowNonStandardFunctions)
{
$this->allowedNonStandardFunctions = $allowNonStandardFunctions;
}
public function setCopyrightNotice($copyrightNotice)
{
$this->copyrightNotice = $copyrightNotice;
}
public function setDefine($define)
{
$this->define = $define;
}
public function setGssFunctionMapProvider($gssFunctionMapProvider)
{
$this->gssFunctionMapProvider = $gssFunctionMapProvider;
}
public function setInputOrientation($inputOrientation)
{
$this->inputOrientation = $inputOrientation;
}
public function setOutputOrientation($outputOrientation)
{
$this->outputOrientation = $outputOrientation;
}
public function setPrettyPrint($prettyPrint)
{
$this->prettyPrint = $prettyPrint;
}
public function filterLoad(AssetInterface $asset)
{
$cleanup = array();
$pb = $this->createProcessBuilder(array(
$this->javaPath,
'-jar',
$this->jarPath,
));
if (null !== $this->allowUnrecognizedFunctions) {
$pb->add('--allow-unrecognized-functions');
}
if (null !== $this->allowedNonStandardFunctions) {
$pb->add('--allowed_non_standard_functions')->add($this->allowedNonStandardFunctions);
}
if (null !== $this->copyrightNotice) {
$pb->add('--copyright-notice')->add($this->copyrightNotice);
}
if (null !== $this->define) {
$pb->add('--define')->add($this->define);
}
if (null !== $this->gssFunctionMapProvider) {
$pb->add('--gss-function-map-provider')->add($this->gssFunctionMapProvider);
}
if (null !== $this->inputOrientation) {
$pb->add('--input-orientation')->add($this->inputOrientation);
}
if (null !== $this->outputOrientation) {
$pb->add('--output-orientation')->add($this->outputOrientation);
}
if (null !== $this->prettyPrint) {
$pb->add('--pretty-print');
}
$pb->add($cleanup[] = $input = FilesystemUtils::createTemporaryFile('gss'));
file_put_contents($input, $asset->getContent());
$proc = $pb->getProcess();
$code = $proc->run();
array_map('unlink', $cleanup);
if (0 !== $code) {
throw FilterException::fromProcess($proc)->setInput($asset->getContent());
}
$asset->setContent($proc->getOutput());
}
public function filterDump(AssetInterface $asset)
{
}
}
src/Assetic/Filter/JSMinFilter.php 0000666 00000001425 13436750441 0013027 0 ustar 00
*/
class JSMinFilter implements FilterInterface
{
public function filterLoad(AssetInterface $asset)
{
}
public function filterDump(AssetInterface $asset)
{
$asset->setContent(\JSMin::minify($asset->getContent()));
}
}
src/Assetic/Filter/CssImportFilter.php 0000666 00000006715 13436750441 0014001 0 ustar 00
*/
class CssImportFilter extends BaseCssFilter implements DependencyExtractorInterface
{
private $importFilter;
/**
* Constructor.
*
* @param FilterInterface $importFilter Filter for each imported asset
*/
public function __construct(FilterInterface $importFilter = null)
{
$this->importFilter = $importFilter ?: new CssRewriteFilter();
}
public function filterLoad(AssetInterface $asset)
{
$importFilter = $this->importFilter;
$sourceRoot = $asset->getSourceRoot();
$sourcePath = $asset->getSourcePath();
$callback = function ($matches) use ($importFilter, $sourceRoot, $sourcePath) {
if (!$matches['url'] || null === $sourceRoot) {
return $matches[0];
}
$importRoot = $sourceRoot;
if (false !== strpos($matches['url'], '://')) {
// absolute
list($importScheme, $tmp) = explode('://', $matches['url'], 2);
list($importHost, $importPath) = explode('/', $tmp, 2);
$importRoot = $importScheme.'://'.$importHost;
} elseif (0 === strpos($matches['url'], '//')) {
// protocol-relative
list($importHost, $importPath) = explode('/', substr($matches['url'], 2), 2);
$importRoot = '//'.$importHost;
} elseif ('/' == $matches['url'][0]) {
// root-relative
$importPath = substr($matches['url'], 1);
} elseif (null !== $sourcePath) {
// document-relative
$importPath = $matches['url'];
if ('.' != $sourceDir = dirname($sourcePath)) {
$importPath = $sourceDir.'/'.$importPath;
}
} else {
return $matches[0];
}
$importSource = $importRoot.'/'.$importPath;
if (false !== strpos($importSource, '://') || 0 === strpos($importSource, '//')) {
$import = new HttpAsset($importSource, array($importFilter), true);
} elseif ('css' != pathinfo($importPath, PATHINFO_EXTENSION) || !file_exists($importSource)) {
// ignore non-css and non-existant imports
return $matches[0];
} else {
$import = new FileAsset($importSource, array($importFilter), $importRoot, $importPath);
}
$import->setTargetPath($sourcePath);
return $import->dump();
};
$content = $asset->getContent();
$lastHash = md5($content);
do {
$content = $this->filterImports($content, $callback);
$hash = md5($content);
} while ($lastHash != $hash && $lastHash = $hash);
$asset->setContent($content);
}
public function filterDump(AssetInterface $asset)
{
}
public function getChildren(AssetFactory $factory, $content, $loadPath = null)
{
// todo
return array();
}
}
src/Assetic/Filter/CallablesFilter.php 0000666 00000002712 13436750441 0013731 0 ustar 00
*/
class CallablesFilter implements FilterInterface, DependencyExtractorInterface
{
private $loader;
private $dumper;
private $extractor;
/**
* @param callable|null $loader
* @param callable|null $dumper
* @param callable|null $extractor
*/
public function __construct($loader = null, $dumper = null, $extractor = null)
{
$this->loader = $loader;
$this->dumper = $dumper;
$this->extractor = $extractor;
}
public function filterLoad(AssetInterface $asset)
{
if (null !== $callable = $this->loader) {
$callable($asset);
}
}
public function filterDump(AssetInterface $asset)
{
if (null !== $callable = $this->dumper) {
$callable($asset);
}
}
public function getChildren(AssetFactory $factory, $content, $loadPath = null)
{
if (null !== $callable = $this->extractor) {
return $callable($factory, $content, $loadPath);
}
return array();
}
}
src/Assetic/Filter/PackerFilter.php 0000666 00000002507 13436750441 0013256 0 ustar 00
*/
class PackerFilter implements FilterInterface
{
protected $encoding = 'None';
protected $fastDecode = true;
protected $specialChars = false;
public function setEncoding($encoding)
{
$this->encoding = $encoding;
}
public function setFastDecode($fastDecode)
{
$this->fastDecode = (bool) $fastDecode;
}
public function setSpecialChars($specialChars)
{
$this->specialChars = (bool) $specialChars;
}
public function filterLoad(AssetInterface $asset)
{
}
public function filterDump(AssetInterface $asset)
{
$packer = new \JavaScriptPacker($asset->getContent(), $this->encoding, $this->fastDecode, $this->specialChars);
$asset->setContent($packer->pack());
}
}
src/Assetic/Filter/CssCacheBustingFilter.php 0000666 00000002675 13436750441 0015067 0 ustar 00
*/
class CssCacheBustingFilter extends BaseCssFilter
{
private $version;
private $format = '%s?%s';
public function setVersion($version)
{
$this->version = $version;
}
public function setFormat($versionFormat)
{
$this->format = $versionFormat;
}
public function filterLoad(AssetInterface $asset)
{
}
public function filterDump(AssetInterface $asset)
{
if (!$this->version) {
return;
}
$version = $this->version;
$format = $this->format;
$asset->setContent($this->filterReferences(
$asset->getContent(),
function ($matches) use ($version, $format) {
if (0 === strpos($matches['url'], 'data:')) {
return $matches[0];
}
return str_replace(
$matches['url'],
sprintf($format, $matches['url'], $version),
$matches[0]
);
}
));
}
}
src/Assetic/Filter/CompassFilter.php 0000666 00000024340 13436750441 0013455 0 ustar 00
*/
class CompassFilter extends BaseSassFilter
{
private $compassPath;
private $rubyPath;
private $scss;
// sass options
private $unixNewlines;
private $debugInfo;
private $cacheLocation;
private $noCache;
// compass options
private $force;
private $style;
private $quiet;
private $boring;
private $noLineComments;
private $imagesDir;
private $javascriptsDir;
private $fontsDir;
private $relativeAssets;
// compass configuration file options
private $plugins = array();
private $httpPath;
private $httpImagesPath;
private $httpFontsPath;
private $httpGeneratedImagesPath;
private $generatedImagesPath;
private $httpJavascriptsPath;
private $homeEnv = true;
public function __construct($compassPath = '/usr/bin/compass', $rubyPath = null)
{
$this->compassPath = $compassPath;
$this->rubyPath = $rubyPath;
$this->cacheLocation = FilesystemUtils::getTemporaryDirectory();
if ('cli' !== php_sapi_name()) {
$this->boring = true;
}
}
public function setScss($scss)
{
$this->scss = $scss;
}
// sass options setters
public function setUnixNewlines($unixNewlines)
{
$this->unixNewlines = $unixNewlines;
}
public function setDebugInfo($debugInfo)
{
$this->debugInfo = $debugInfo;
}
public function setCacheLocation($cacheLocation)
{
$this->cacheLocation = $cacheLocation;
}
public function setNoCache($noCache)
{
$this->noCache = $noCache;
}
// compass options setters
public function setForce($force)
{
$this->force = $force;
}
public function setStyle($style)
{
$this->style = $style;
}
public function setQuiet($quiet)
{
$this->quiet = $quiet;
}
public function setBoring($boring)
{
$this->boring = $boring;
}
public function setNoLineComments($noLineComments)
{
$this->noLineComments = $noLineComments;
}
public function setImagesDir($imagesDir)
{
$this->imagesDir = $imagesDir;
}
public function setJavascriptsDir($javascriptsDir)
{
$this->javascriptsDir = $javascriptsDir;
}
public function setFontsDir($fontsDir)
{
$this->fontsDir = $fontsDir;
}
// compass configuration file options setters
public function setPlugins(array $plugins)
{
$this->plugins = $plugins;
}
public function addPlugin($plugin)
{
$this->plugins[] = $plugin;
}
public function setHttpPath($httpPath)
{
$this->httpPath = $httpPath;
}
public function setHttpImagesPath($httpImagesPath)
{
$this->httpImagesPath = $httpImagesPath;
}
public function setHttpFontsPath($httpFontsPath)
{
$this->httpFontsPath = $httpFontsPath;
}
public function setHttpGeneratedImagesPath($httpGeneratedImagesPath)
{
$this->httpGeneratedImagesPath = $httpGeneratedImagesPath;
}
public function setGeneratedImagesPath($generatedImagesPath)
{
$this->generatedImagesPath = $generatedImagesPath;
}
public function setHttpJavascriptsPath($httpJavascriptsPath)
{
$this->httpJavascriptsPath = $httpJavascriptsPath;
}
public function setHomeEnv($homeEnv)
{
$this->homeEnv = $homeEnv;
}
public function setRelativeAssets($relativeAssets)
{
$this->relativeAssets = $relativeAssets;
}
public function filterLoad(AssetInterface $asset)
{
$loadPaths = $this->loadPaths;
if ($dir = $asset->getSourceDirectory()) {
$loadPaths[] = $dir;
}
$tempDir = $this->cacheLocation ? $this->cacheLocation : FilesystemUtils::getTemporaryDirectory();
$compassProcessArgs = array(
$this->compassPath,
'compile',
$tempDir,
);
if (null !== $this->rubyPath) {
$compassProcessArgs = array_merge(explode(' ', $this->rubyPath), $compassProcessArgs);
}
$pb = $this->createProcessBuilder($compassProcessArgs);
if ($this->force) {
$pb->add('--force');
}
if ($this->style) {
$pb->add('--output-style')->add($this->style);
}
if ($this->quiet) {
$pb->add('--quiet');
}
if ($this->boring) {
$pb->add('--boring');
}
if ($this->noLineComments) {
$pb->add('--no-line-comments');
}
// these three options are not passed into the config file
// because like this, compass adapts this to be xxx_dir or xxx_path
// whether it's an absolute path or not
if ($this->imagesDir) {
$pb->add('--images-dir')->add($this->imagesDir);
}
if ($this->relativeAssets) {
$pb->add('--relative-assets');
}
if ($this->javascriptsDir) {
$pb->add('--javascripts-dir')->add($this->javascriptsDir);
}
if ($this->fontsDir) {
$pb->add('--fonts-dir')->add($this->fontsDir);
}
// options in config file
$optionsConfig = array();
if (!empty($loadPaths)) {
$optionsConfig['additional_import_paths'] = $loadPaths;
}
if ($this->unixNewlines) {
$optionsConfig['sass_options']['unix_newlines'] = true;
}
if ($this->debugInfo) {
$optionsConfig['sass_options']['debug_info'] = true;
}
if ($this->cacheLocation) {
$optionsConfig['sass_options']['cache_location'] = $this->cacheLocation;
}
if ($this->noCache) {
$optionsConfig['sass_options']['no_cache'] = true;
}
if ($this->httpPath) {
$optionsConfig['http_path'] = $this->httpPath;
}
if ($this->httpImagesPath) {
$optionsConfig['http_images_path'] = $this->httpImagesPath;
}
if ($this->httpFontsPath) {
$optionsConfig['http_fonts_path'] = $this->httpFontsPath;
}
if ($this->httpGeneratedImagesPath) {
$optionsConfig['http_generated_images_path'] = $this->httpGeneratedImagesPath;
}
if ($this->generatedImagesPath) {
$optionsConfig['generated_images_path'] = $this->generatedImagesPath;
}
if ($this->httpJavascriptsPath) {
$optionsConfig['http_javascripts_path'] = $this->httpJavascriptsPath;
}
// options in configuration file
if (count($optionsConfig)) {
$config = array();
foreach ($this->plugins as $plugin) {
$config[] = sprintf("require '%s'", addcslashes($plugin, '\\'));
}
foreach ($optionsConfig as $name => $value) {
if (!is_array($value)) {
$config[] = sprintf('%s = "%s"', $name, addcslashes($value, '\\'));
} elseif (!empty($value)) {
$config[] = sprintf('%s = %s', $name, $this->formatArrayToRuby($value));
}
}
$configFile = tempnam($tempDir, 'assetic_compass');
file_put_contents($configFile, implode("\n", $config)."\n");
$pb->add('--config')->add($configFile);
}
$pb->add('--sass-dir')->add('')->add('--css-dir')->add('');
// compass choose the type (sass or scss from the filename)
if (null !== $this->scss) {
$type = $this->scss ? 'scss' : 'sass';
} elseif ($path = $asset->getSourcePath()) {
// FIXME: what if the extension is something else?
$type = pathinfo($path, PATHINFO_EXTENSION);
} else {
$type = 'scss';
}
$tempName = tempnam($tempDir, 'assetic_compass');
unlink($tempName); // FIXME: don't use tempnam() here
// input
$input = $tempName.'.'.$type;
// work-around for https://github.com/chriseppstein/compass/issues/748
if (defined('PHP_WINDOWS_VERSION_MAJOR')) {
$input = str_replace('\\', '/', $input);
}
$pb->add($input);
file_put_contents($input, $asset->getContent());
// output
$output = $tempName.'.css';
if ($this->homeEnv) {
// it's not really usefull but... https://github.com/chriseppstein/compass/issues/376
$pb->setEnv('HOME', FilesystemUtils::getTemporaryDirectory());
$this->mergeEnv($pb);
}
$proc = $pb->getProcess();
$code = $proc->run();
if (0 !== $code) {
unlink($input);
if (isset($configFile)) {
unlink($configFile);
}
throw FilterException::fromProcess($proc)->setInput($asset->getContent());
}
$asset->setContent(file_get_contents($output));
unlink($input);
unlink($output);
if (isset($configFile)) {
unlink($configFile);
}
}
public function filterDump(AssetInterface $asset)
{
}
private function formatArrayToRuby($array)
{
$output = array();
// does we have an associative array ?
if (count(array_filter(array_keys($array), "is_numeric")) != count($array)) {
foreach ($array as $name => $value) {
$output[] = sprintf(' :%s => "%s"', $name, addcslashes($value, '\\'));
}
$output = "{\n".implode(",\n", $output)."\n}";
} else {
foreach ($array as $name => $value) {
$output[] = sprintf(' "%s"', addcslashes($value, '\\'));
}
$output = "[\n".implode(",\n", $output)."\n]";
}
return $output;
}
}
src/Assetic/Filter/SprocketsFilter.php 0000666 00000007332 13436750441 0014027 0 ustar 00
*/
class SprocketsFilter extends BaseProcessFilter implements DependencyExtractorInterface
{
private $sprocketsLib;
private $rubyBin;
private $includeDirs;
private $assetRoot;
/**
* Constructor.
*
* @param string $sprocketsLib Path to the Sprockets lib/ directory
* @param string $rubyBin Path to the ruby binary
*/
public function __construct($sprocketsLib = null, $rubyBin = '/usr/bin/ruby')
{
$this->sprocketsLib = $sprocketsLib;
$this->rubyBin = $rubyBin;
$this->includeDirs = array();
}
public function addIncludeDir($directory)
{
$this->includeDirs[] = $directory;
}
public function setAssetRoot($assetRoot)
{
$this->assetRoot = $assetRoot;
}
/**
* Hack around a bit, get the job done.
*/
public function filterLoad(AssetInterface $asset)
{
static $format = <<<'EOF'
#!/usr/bin/env ruby
require %s
%s
options = { :load_path => [],
:source_files => [%s],
:expand_paths => false }
%ssecretary = Sprockets::Secretary.new(options)
secretary.install_assets if options[:asset_root]
print secretary.concatenation
EOF;
$more = '';
foreach ($this->includeDirs as $directory) {
$more .= 'options[:load_path] << '.var_export($directory, true)."\n";
}
if (null !== $this->assetRoot) {
$more .= 'options[:asset_root] = '.var_export($this->assetRoot, true)."\n";
}
if ($more) {
$more .= "\n";
}
$tmpAsset = FilesystemUtils::createTemporaryFile('sprockets_asset');
file_put_contents($tmpAsset, $asset->getContent());
$input = FilesystemUtils::createTemporaryFile('sprockets_in');
file_put_contents($input, sprintf($format,
$this->sprocketsLib
? sprintf('File.join(%s, \'sprockets\')', var_export($this->sprocketsLib, true))
: '\'sprockets\'',
$this->getHack($asset),
var_export($tmpAsset, true),
$more
));
$pb = $this->createProcessBuilder(array(
$this->rubyBin,
$input,
));
$proc = $pb->getProcess();
$code = $proc->run();
unlink($tmpAsset);
unlink($input);
if (0 !== $code) {
throw FilterException::fromProcess($proc)->setInput($asset->getContent());
}
$asset->setContent($proc->getOutput());
}
public function filterDump(AssetInterface $asset)
{
}
public function getChildren(AssetFactory $factory, $content, $loadPath = null)
{
// todo
return array();
}
private function getHack(AssetInterface $asset)
{
static $format = <<<'EOF'
module Sprockets
class Preprocessor
protected
def pathname_for_relative_require_from(source_line)
Sprockets::Pathname.new(@environment, File.join(%s, location_from(source_line)))
end
end
end
EOF;
if ($dir = $asset->getSourceDirectory()) {
return sprintf($format, var_export($dir, true));
}
}
}
src/Assetic/Filter/AutoprefixerFilter.php 0000666 00000003746 13436750441 0014534 0 ustar 00
*/
class AutoprefixerFilter extends BaseNodeFilter
{
/**
* @var string
*/
private $autoprefixerBin;
/**
* @var array
*/
private $browsers = array();
public function __construct($autoprefixerBin)
{
$this->autoprefixerBin = $autoprefixerBin;
}
/**
* @param array $browsers
*/
public function setBrowsers(array $browsers)
{
$this->browsers = $browsers;
}
/**
* @param string $browser
*/
public function addBrowser($browser)
{
$this->browsers[] = $browser;
}
public function filterLoad(AssetInterface $asset)
{
$input = $asset->getContent();
$pb = $this->createProcessBuilder(array($this->autoprefixerBin));
$pb->setInput($input);
if ($this->browsers) {
$pb->add('-b')->add(implode(',', $this->browsers));
}
$output = FilesystemUtils::createTemporaryFile('autoprefixer');
$pb->add('-o')->add($output);
$proc = $pb->getProcess();
if (0 !== $proc->run()) {
throw FilterException::fromProcess($proc)->setInput($asset->getContent());
}
$asset->setContent(file_get_contents($output));
unlink($output);
}
/**
* Filters an asset just before it's dumped.
*
* @param AssetInterface $asset An asset
*/
public function filterDump(AssetInterface $asset)
{
}
}
src/Assetic/Filter/MinifyCssCompressorFilter.php 0000666 00000001636 13436750441 0016034 0 ustar 00
* @author http://code.google.com/u/1stvamp/ (Issue 64 patch)
*/
class MinifyCssCompressorFilter implements FilterInterface
{
public function filterLoad(AssetInterface $asset)
{
}
public function filterDump(AssetInterface $asset)
{
$asset->setContent(\Minify_CSS_Compressor::process($asset->getContent()));
}
}
src/Assetic/Filter/SeparatorFilter.php 0000666 00000001770 13436750441 0014012 0 ustar 00
*/
class SeparatorFilter implements FilterInterface
{
/**
* @var string
*/
private $separator;
/**
* Constructor.
*
* @param string $separator Separator to use between assets
*/
public function __construct($separator = ';')
{
$this->separator = $separator;
}
public function filterLoad(AssetInterface $asset)
{
}
public function filterDump(AssetInterface $asset)
{
$asset->setContent($asset->getContent() . $this->separator);
}
}
src/Assetic/Filter/GoogleClosure/BaseCompilerFilter.php 0000666 00000004747 13436750441 0017177 0 ustar 00
*/
abstract class BaseCompilerFilter implements FilterInterface
{
// compilation levels
const COMPILE_WHITESPACE_ONLY = 'WHITESPACE_ONLY';
const COMPILE_SIMPLE_OPTIMIZATIONS = 'SIMPLE_OPTIMIZATIONS';
const COMPILE_ADVANCED_OPTIMIZATIONS = 'ADVANCED_OPTIMIZATIONS';
// formatting modes
const FORMAT_PRETTY_PRINT = 'pretty_print';
const FORMAT_PRINT_INPUT_DELIMITER = 'print_input_delimiter';
// warning levels
const LEVEL_QUIET = 'QUIET';
const LEVEL_DEFAULT = 'DEFAULT';
const LEVEL_VERBOSE = 'VERBOSE';
// languages
const LANGUAGE_ECMASCRIPT3 = 'ECMASCRIPT3';
const LANGUAGE_ECMASCRIPT5 = 'ECMASCRIPT5';
const LANGUAGE_ECMASCRIPT5_STRICT = 'ECMASCRIPT5_STRICT';
protected $timeout;
protected $compilationLevel;
protected $jsExterns;
protected $externsUrl;
protected $excludeDefaultExterns;
protected $formatting;
protected $useClosureLibrary;
protected $warningLevel;
protected $language;
public function setTimeout($timeout)
{
$this->timeout = $timeout;
}
public function setCompilationLevel($compilationLevel)
{
$this->compilationLevel = $compilationLevel;
}
public function setJsExterns($jsExterns)
{
$this->jsExterns = $jsExterns;
}
public function setExternsUrl($externsUrl)
{
$this->externsUrl = $externsUrl;
}
public function setExcludeDefaultExterns($excludeDefaultExterns)
{
$this->excludeDefaultExterns = $excludeDefaultExterns;
}
public function setFormatting($formatting)
{
$this->formatting = $formatting;
}
public function setUseClosureLibrary($useClosureLibrary)
{
$this->useClosureLibrary = $useClosureLibrary;
}
public function setWarningLevel($warningLevel)
{
$this->warningLevel = $warningLevel;
}
public function setLanguage($language)
{
$this->language = $language;
}
public function filterLoad(AssetInterface $asset)
{
}
}
src/Assetic/Filter/GoogleClosure/CompilerJarFilter.php 0000666 00000006310 13436750441 0017025 0 ustar 00
*/
class CompilerJarFilter extends BaseCompilerFilter
{
private $jarPath;
private $javaPath;
private $flagFile;
public function __construct($jarPath, $javaPath = '/usr/bin/java')
{
$this->jarPath = $jarPath;
$this->javaPath = $javaPath;
}
public function setFlagFile($flagFile)
{
$this->flagFile = $flagFile;
}
public function filterDump(AssetInterface $asset)
{
$is64bit = PHP_INT_SIZE === 8;
$cleanup = array();
$pb = new ProcessBuilder(array_merge(
array($this->javaPath),
$is64bit
? array('-server', '-XX:+TieredCompilation')
: array('-client', '-d32'),
array('-jar', $this->jarPath)
));
if (null !== $this->timeout) {
$pb->setTimeout($this->timeout);
}
if (null !== $this->compilationLevel) {
$pb->add('--compilation_level')->add($this->compilationLevel);
}
if (null !== $this->jsExterns) {
$cleanup[] = $externs = FilesystemUtils::createTemporaryFile('google_closure');
file_put_contents($externs, $this->jsExterns);
$pb->add('--externs')->add($externs);
}
if (null !== $this->externsUrl) {
$cleanup[] = $externs = FilesystemUtils::createTemporaryFile('google_closure');
file_put_contents($externs, file_get_contents($this->externsUrl));
$pb->add('--externs')->add($externs);
}
if (null !== $this->excludeDefaultExterns) {
$pb->add('--use_only_custom_externs');
}
if (null !== $this->formatting) {
$pb->add('--formatting')->add($this->formatting);
}
if (null !== $this->useClosureLibrary) {
$pb->add('--manage_closure_dependencies');
}
if (null !== $this->warningLevel) {
$pb->add('--warning_level')->add($this->warningLevel);
}
if (null !== $this->language) {
$pb->add('--language_in')->add($this->language);
}
if (null !== $this->flagFile) {
$pb->add('--flagfile')->add($this->flagFile);
}
$pb->add('--js')->add($cleanup[] = $input = FilesystemUtils::createTemporaryFile('google_closure'));
file_put_contents($input, $asset->getContent());
$proc = $pb->getProcess();
$code = $proc->run();
array_map('unlink', $cleanup);
if (0 !== $code) {
throw FilterException::fromProcess($proc)->setInput($asset->getContent());
}
$asset->setContent($proc->getOutput());
}
}
src/Assetic/Filter/GoogleClosure/CompilerApiFilter.php 0000666 00000010740 13436750441 0017024 0 ustar 00
*/
class CompilerApiFilter extends BaseCompilerFilter
{
private $proxy;
private $proxyFullUri;
public function setProxy($proxy)
{
$this->proxy = $proxy;
}
public function setProxyFullUri($proxyFullUri)
{
$this->proxyFullUri = $proxyFullUri;
}
public function filterDump(AssetInterface $asset)
{
$query = array(
'js_code' => $asset->getContent(),
'output_format' => 'json',
'output_info' => 'compiled_code',
);
if (null !== $this->compilationLevel) {
$query['compilation_level'] = $this->compilationLevel;
}
if (null !== $this->jsExterns) {
$query['js_externs'] = $this->jsExterns;
}
if (null !== $this->externsUrl) {
$query['externs_url'] = $this->externsUrl;
}
if (null !== $this->excludeDefaultExterns) {
$query['exclude_default_externs'] = $this->excludeDefaultExterns ? 'true' : 'false';
}
if (null !== $this->formatting) {
$query['formatting'] = $this->formatting;
}
if (null !== $this->useClosureLibrary) {
$query['use_closure_library'] = $this->useClosureLibrary ? 'true' : 'false';
}
if (null !== $this->warningLevel) {
$query['warning_level'] = $this->warningLevel;
}
if (null !== $this->language) {
$query['language'] = $this->language;
}
if (preg_match('/1|yes|on|true/i', ini_get('allow_url_fopen'))) {
$contextOptions = array('http' => array(
'method' => 'POST',
'header' => 'Content-Type: application/x-www-form-urlencoded',
'content' => http_build_query($query),
));
if (null !== $this->timeout) {
$contextOptions['http']['timeout'] = $this->timeout;
}
if ($this->proxy) {
$contextOptions['http']['proxy'] = $this->proxy;
$contextOptions['http']['request_fulluri'] = (Boolean) $this->proxyFullUri;
}
$context = stream_context_create($contextOptions);
$response = file_get_contents('http://closure-compiler.appspot.com/compile', false, $context);
$data = json_decode($response);
} elseif (defined('CURLOPT_POST') && !in_array('curl_init', explode(',', ini_get('disable_functions')))) {
$ch = curl_init('http://closure-compiler.appspot.com/compile');
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-type: application/x-www-form-urlencoded'));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $query);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 15);
if (null !== $this->timeout) {
curl_setopt($ch, CURLOPT_TIMEOUT, $this->timeout);
}
if ($this->proxy) {
curl_setopt($ch, CURLOPT_HTTPPROXYTUNNEL, true);
curl_setopt($ch, CURLOPT_PROXY, $this->proxy);
}
$response = curl_exec($ch);
curl_close($ch);
$data = json_decode($response);
} else {
throw new \RuntimeException("There is no known way to contact closure compiler available");
}
if (isset($data->serverErrors) && 0 < count($data->serverErrors)) {
// @codeCoverageIgnoreStart
throw new \RuntimeException(sprintf('The Google Closure Compiler API threw some server errors: '.print_r($data->serverErrors, true)));
// @codeCoverageIgnoreEnd
}
if (isset($data->errors) && 0 < count($data->errors)) {
// @codeCoverageIgnoreStart
throw new \RuntimeException(sprintf('The Google Closure Compiler API threw some errors: '.print_r($data->errors, true)));
// @codeCoverageIgnoreEnd
}
$asset->setContent($data->compiledCode);
}
}
src/Assetic/Filter/BaseProcessFilter.php 0000666 00000002454 13436750441 0014263 0 ustar 00 timeout = $timeout;
}
/**
* Creates a new process builder.
*
* @param array $arguments An optional array of arguments
*
* @return ProcessBuilder A new process builder
*/
protected function createProcessBuilder(array $arguments = array())
{
$pb = new ProcessBuilder($arguments);
if (null !== $this->timeout) {
$pb->setTimeout($this->timeout);
}
return $pb;
}
protected function mergeEnv(ProcessBuilder $pb)
{
foreach (array_filter($_SERVER, 'is_scalar') as $key => $value) {
$pb->setEnv($key, $value);
}
}
}
src/Assetic/Filter/PhpCssEmbedFilter.php 0000666 00000002255 13436750441 0014206 0 ustar 00
* @link https://github.com/krichprollsch/phpCssEmbed
*/
class PhpCssEmbedFilter implements DependencyExtractorInterface
{
private $presets = array();
public function setPresets(array $presets)
{
$this->presets = $presets;
}
public function filterLoad(AssetInterface $asset)
{
$pce = new CssEmbed();
if ($dir = $asset->getSourceDirectory()) {
$pce->setRootDir($dir);
}
$asset->setContent($pce->embedString($asset->getContent()));
}
public function filterDump(AssetInterface $asset)
{
}
public function getChildren(AssetFactory $factory, $content, $loadPath = null)
{
// todo
return array();
}
}
src/Assetic/Filter/LessphpFilter.php 0000666 00000010161 13436750441 0013462 0 ustar 00
* @author Kris Wallsmith
*/
class LessphpFilter implements DependencyExtractorInterface
{
private $presets = array();
private $formatter;
private $preserveComments;
private $customFunctions = array();
private $options = array();
/**
* Lessphp Load Paths
*
* @var array
*/
protected $loadPaths = array();
/**
* Adds a load path to the paths used by lessphp
*
* @param string $path Load Path
*/
public function addLoadPath($path)
{
$this->loadPaths[] = $path;
}
/**
* Sets load paths used by lessphp
*
* @param array $loadPaths Load paths
*/
public function setLoadPaths(array $loadPaths)
{
$this->loadPaths = $loadPaths;
}
public function setPresets(array $presets)
{
$this->presets = $presets;
}
public function setOptions(array $options)
{
$this->options = $options;
}
/**
* @param string $formatter One of "lessjs", "compressed", or "classic".
*/
public function setFormatter($formatter)
{
$this->formatter = $formatter;
}
/**
* @param boolean $preserveComments
*/
public function setPreserveComments($preserveComments)
{
$this->preserveComments = $preserveComments;
}
public function filterLoad(AssetInterface $asset)
{
$lc = new \lessc();
if ($dir = $asset->getSourceDirectory()) {
$lc->importDir = $dir;
}
foreach ($this->loadPaths as $loadPath) {
$lc->addImportDir($loadPath);
}
foreach ($this->customFunctions as $name => $callable) {
$lc->registerFunction($name, $callable);
}
if ($this->formatter) {
$lc->setFormatter($this->formatter);
}
if (null !== $this->preserveComments) {
$lc->setPreserveComments($this->preserveComments);
}
if (method_exists($lc, 'setOptions') && count($this->options) > 0 ) {
$lc->setOptions($this->options);
}
$asset->setContent($lc->parse($asset->getContent(), $this->presets));
}
public function registerFunction($name, $callable)
{
$this->customFunctions[$name] = $callable;
}
public function filterDump(AssetInterface $asset)
{
}
public function getChildren(AssetFactory $factory, $content, $loadPath = null)
{
$loadPaths = $this->loadPaths;
if (null !== $loadPath) {
$loadPaths[] = $loadPath;
}
if (empty($loadPaths)) {
return array();
}
$children = array();
foreach (LessUtils::extractImports($content) as $reference) {
if ('.css' === substr($reference, -4)) {
// skip normal css imports
// todo: skip imports with media queries
continue;
}
if ('.less' !== substr($reference, -5)) {
$reference .= '.less';
}
foreach ($loadPaths as $loadPath) {
if (file_exists($file = $loadPath.'/'.$reference)) {
$coll = $factory->createAsset($file, array(), array('root' => $loadPath));
foreach ($coll as $leaf) {
$leaf->ensureFilter($this);
$children[] = $leaf;
goto next_reference;
}
}
}
next_reference:
}
return $children;
}
}
src/Assetic/Filter/JpegoptimFilter.php 0000666 00000003534 13436750441 0014010 0 ustar 00
*/
class JpegoptimFilter extends BaseProcessFilter
{
private $jpegoptimBin;
private $stripAll;
private $max;
/**
* Constructor.
*
* @param string $jpegoptimBin Path to the jpegoptim binary
*/
public function __construct($jpegoptimBin = '/usr/bin/jpegoptim')
{
$this->jpegoptimBin = $jpegoptimBin;
}
public function setStripAll($stripAll)
{
$this->stripAll = $stripAll;
}
public function setMax($max)
{
$this->max = $max;
}
public function filterLoad(AssetInterface $asset)
{
}
public function filterDump(AssetInterface $asset)
{
$pb = $this->createProcessBuilder(array($this->jpegoptimBin));
if ($this->stripAll) {
$pb->add('--strip-all');
}
if ($this->max) {
$pb->add('--max='.$this->max);
}
$pb->add($input = FilesystemUtils::createTemporaryFile('jpegoptim'));
file_put_contents($input, $asset->getContent());
$proc = $pb->getProcess();
$proc->run();
if (false !== strpos($proc->getOutput(), 'ERROR')) {
unlink($input);
throw FilterException::fromProcess($proc)->setInput($asset->getContent());
}
$asset->setContent(file_get_contents($input));
unlink($input);
}
}
src/Assetic/Filter/RooleFilter.php 0000666 00000004010 13436750441 0013120 0 ustar 00
*/
class RooleFilter extends BaseNodeFilter implements DependencyExtractorInterface
{
private $rooleBin;
private $nodeBin;
/**
* Constructor
*
* @param string $rooleBin The path to the roole binary
* @param string $nodeBin The path to the node binary
*/
public function __construct($rooleBin = '/usr/bin/roole', $nodeBin = null)
{
$this->rooleBin = $rooleBin;
$this->nodeBin = $nodeBin;
}
public function filterLoad(AssetInterface $asset)
{
$input = FilesystemUtils::createTemporaryFile('roole');
$output = $input.'.css';
file_put_contents($input, $asset->getContent());
$pb = $this->createProcessBuilder($this->nodeBin
? array($this->nodeBin, $this->rooleBin)
: array($this->rooleBin));
$pb->add($input);
$proc = $pb->getProcess();
$code = $proc->run();
unlink($input);
if (0 !== $code) {
if (file_exists($output)) {
unlink($output);
}
throw FilterException::fromProcess($proc);
}
if (!file_exists($output)) {
throw new \RuntimeException('Error creating output file.');
}
$asset->setContent(file_get_contents($output));
unlink($output);
}
public function filterDump(AssetInterface $asset)
{
}
public function getChildren(AssetFactory $factory, $content, $loadPath = null)
{
// todo
return array();
}
}
src/Assetic/Filter/JSMinPlusFilter.php 0000666 00000001445 13436750441 0013675 0 ustar 00
*/
class JSMinPlusFilter implements FilterInterface
{
public function filterLoad(AssetInterface $asset)
{
}
public function filterDump(AssetInterface $asset)
{
$asset->setContent(\JSMinPlus::minify($asset->getContent()));
}
}
src/Assetic/Filter/CssRewriteFilter.php 0000666 00000006723 13436750441 0014147 0 ustar 00
*/
class CssRewriteFilter extends BaseCssFilter
{
public function filterLoad(AssetInterface $asset)
{
}
public function filterDump(AssetInterface $asset)
{
$sourceBase = $asset->getSourceRoot();
$sourcePath = $asset->getSourcePath();
$targetPath = $asset->getTargetPath();
if (null === $sourcePath || null === $targetPath || $sourcePath == $targetPath) {
return;
}
// learn how to get from the target back to the source
if (false !== strpos($sourceBase, '://')) {
list($scheme, $url) = explode('://', $sourceBase.'/'.$sourcePath, 2);
list($host, $path) = explode('/', $url, 2);
$host = $scheme.'://'.$host.'/';
$path = false === strpos($path, '/') ? '' : dirname($path);
$path .= '/';
} else {
// assume source and target are on the same host
$host = '';
// pop entries off the target until it fits in the source
if ('.' == dirname($sourcePath)) {
$path = str_repeat('../', substr_count($targetPath, '/'));
} elseif ('.' == $targetDir = dirname($targetPath)) {
$path = dirname($sourcePath).'/';
} else {
$path = '';
while (0 !== strpos($sourcePath, $targetDir)) {
if (false !== $pos = strrpos($targetDir, '/')) {
$targetDir = substr($targetDir, 0, $pos);
$path .= '../';
} else {
$targetDir = '';
$path .= '../';
break;
}
}
$path .= ltrim(substr(dirname($sourcePath).'/', strlen($targetDir)), '/');
}
}
$content = $this->filterReferences($asset->getContent(), function ($matches) use ($host, $path) {
if (false !== strpos($matches['url'], '://') || 0 === strpos($matches['url'], '//') || 0 === strpos($matches['url'], 'data:')) {
// absolute or protocol-relative or data uri
return $matches[0];
}
if (isset($matches['url'][0]) && '/' == $matches['url'][0]) {
// root relative
return str_replace($matches['url'], $host.$matches['url'], $matches[0]);
}
// document relative
$url = $matches['url'];
while (0 === strpos($url, '../') && 2 <= substr_count($path, '/')) {
$path = substr($path, 0, strrpos(rtrim($path, '/'), '/') + 1);
$url = substr($url, 3);
}
$parts = array();
foreach (explode('/', $host.$path.$url) as $part) {
if ('..' === $part && count($parts) && '..' !== end($parts)) {
array_pop($parts);
} else {
$parts[] = $part;
}
}
return str_replace($matches['url'], implode('/', $parts), $matches[0]);
});
$asset->setContent($content);
}
}
src/Assetic/Filter/FilterInterface.php 0000666 00000001443 13436750441 0013747 0 ustar 00
*/
interface FilterInterface
{
/**
* Filters an asset after it has been loaded.
*
* @param AssetInterface $asset An asset
*/
public function filterLoad(AssetInterface $asset);
/**
* Filters an asset just before it's dumped.
*
* @param AssetInterface $asset An asset
*/
public function filterDump(AssetInterface $asset);
}
src/Assetic/Filter/JpegtranFilter.php 0000666 00000004435 13436750441 0013625 0 ustar 00
*/
class JpegtranFilter extends BaseProcessFilter
{
const COPY_NONE = 'none';
const COPY_COMMENTS = 'comments';
const COPY_ALL = 'all';
private $jpegtranBin;
private $optimize;
private $copy;
private $progressive;
private $restart;
/**
* Constructor.
*
* @param string $jpegtranBin Path to the jpegtran binary
*/
public function __construct($jpegtranBin = '/usr/bin/jpegtran')
{
$this->jpegtranBin = $jpegtranBin;
}
public function setOptimize($optimize)
{
$this->optimize = $optimize;
}
public function setCopy($copy)
{
$this->copy = $copy;
}
public function setProgressive($progressive)
{
$this->progressive = $progressive;
}
public function setRestart($restart)
{
$this->restart = $restart;
}
public function filterLoad(AssetInterface $asset)
{
}
public function filterDump(AssetInterface $asset)
{
$pb = $this->createProcessBuilder(array($this->jpegtranBin));
if ($this->optimize) {
$pb->add('-optimize');
}
if ($this->copy) {
$pb->add('-copy')->add($this->copy);
}
if ($this->progressive) {
$pb->add('-progressive');
}
if (null !== $this->restart) {
$pb->add('-restart')->add($this->restart);
}
$pb->add($input = FilesystemUtils::createTemporaryFile('jpegtran'));
file_put_contents($input, $asset->getContent());
$proc = $pb->getProcess();
$code = $proc->run();
unlink($input);
if (0 !== $code) {
throw FilterException::fromProcess($proc)->setInput($asset->getContent());
}
$asset->setContent($proc->getOutput());
}
}
src/Assetic/Filter/CssEmbedFilter.php 0000666 00000007117 13436750441 0013540 0 ustar 00
*/
class CssEmbedFilter extends BaseProcessFilter implements DependencyExtractorInterface
{
private $jarPath;
private $javaPath;
private $charset;
private $mhtml; // Enable MHTML mode.
private $mhtmlRoot; // Use as the MHTML root for the file.
private $root; // Prepends to all relative URLs.
private $skipMissing; // Don't throw an error for missing image files.
private $maxUriLength; // Maximum length for a data URI. Defaults to 32768.
private $maxImageSize; // Maximum image size (in bytes) to convert.
public function __construct($jarPath, $javaPath = '/usr/bin/java')
{
$this->jarPath = $jarPath;
$this->javaPath = $javaPath;
}
public function setCharset($charset)
{
$this->charset = $charset;
}
public function setMhtml($mhtml)
{
$this->mhtml = $mhtml;
}
public function setMhtmlRoot($mhtmlRoot)
{
$this->mhtmlRoot = $mhtmlRoot;
}
public function setRoot($root)
{
$this->root = $root;
}
public function setSkipMissing($skipMissing)
{
$this->skipMissing = $skipMissing;
}
public function setMaxUriLength($maxUriLength)
{
$this->maxUriLength = $maxUriLength;
}
public function setMaxImageSize($maxImageSize)
{
$this->maxImageSize = $maxImageSize;
}
public function filterLoad(AssetInterface $asset)
{
}
public function filterDump(AssetInterface $asset)
{
$pb = $this->createProcessBuilder(array(
$this->javaPath,
'-jar',
$this->jarPath,
));
if (null !== $this->charset) {
$pb->add('--charset')->add($this->charset);
}
if ($this->mhtml) {
$pb->add('--mhtml');
}
if (null !== $this->mhtmlRoot) {
$pb->add('--mhtmlroot')->add($this->mhtmlRoot);
}
// automatically define root if not already defined
if (null === $this->root) {
if ($dir = $asset->getSourceDirectory()) {
$pb->add('--root')->add($dir);
}
} else {
$pb->add('--root')->add($this->root);
}
if ($this->skipMissing) {
$pb->add('--skip-missing');
}
if (null !== $this->maxUriLength) {
$pb->add('--max-uri-length')->add($this->maxUriLength);
}
if (null !== $this->maxImageSize) {
$pb->add('--max-image-size')->add($this->maxImageSize);
}
// input
$pb->add($input = FilesystemUtils::createTemporaryFile('cssembed'));
file_put_contents($input, $asset->getContent());
$proc = $pb->getProcess();
$code = $proc->run();
unlink($input);
if (0 !== $code) {
throw FilterException::fromProcess($proc)->setInput($asset->getContent());
}
$asset->setContent($proc->getOutput());
}
public function getChildren(AssetFactory $factory, $content, $loadPath = null)
{
// todo
return array();
}
}
src/Assetic/Filter/FilterCollection.php 0000666 00000003451 13436750441 0014143 0 ustar 00
*/
class FilterCollection implements FilterInterface, \IteratorAggregate, \Countable
{
private $filters = array();
public function __construct($filters = array())
{
foreach ($filters as $filter) {
$this->ensure($filter);
}
}
/**
* Checks that the current collection contains the supplied filter.
*
* If the supplied filter is another filter collection, each of its
* filters will be checked.
*/
public function ensure(FilterInterface $filter)
{
if ($filter instanceof \Traversable) {
foreach ($filter as $f) {
$this->ensure($f);
}
} elseif (!in_array($filter, $this->filters, true)) {
$this->filters[] = $filter;
}
}
public function all()
{
return $this->filters;
}
public function clear()
{
$this->filters = array();
}
public function filterLoad(AssetInterface $asset)
{
foreach ($this->filters as $filter) {
$filter->filterLoad($asset);
}
}
public function filterDump(AssetInterface $asset)
{
foreach ($this->filters as $filter) {
$filter->filterDump($asset);
}
}
public function getIterator()
{
return new \ArrayIterator($this->filters);
}
public function count()
{
return count($this->filters);
}
}
src/Assetic/Filter/HandlebarsFilter.php 0000666 00000005424 13436750441 0014115 0 ustar 00
*/
class HandlebarsFilter extends BaseNodeFilter
{
private $handlebarsBin;
private $nodeBin;
private $minimize = false;
private $simple = false;
public function __construct($handlebarsBin = '/usr/bin/handlebars', $nodeBin = null)
{
$this->handlebarsBin = $handlebarsBin;
$this->nodeBin = $nodeBin;
}
public function setMinimize($minimize)
{
$this->minimize = $minimize;
}
public function setSimple($simple)
{
$this->simple = $simple;
}
public function filterLoad(AssetInterface $asset)
{
$pb = $this->createProcessBuilder($this->nodeBin
? array($this->nodeBin, $this->handlebarsBin)
: array($this->handlebarsBin));
if ($sourcePath = $asset->getSourcePath()) {
$templateName = basename($sourcePath);
} else {
throw new \LogicException('The handlebars filter requires that assets have a source path set');
}
$inputDirPath = FilesystemUtils::createThrowAwayDirectory('handlebars_in');
$inputPath = $inputDirPath.DIRECTORY_SEPARATOR.$templateName;
$outputPath = FilesystemUtils::createTemporaryFile('handlebars_out');
file_put_contents($inputPath, $asset->getContent());
$pb->add($inputPath)->add('-f')->add($outputPath);
if ($this->minimize) {
$pb->add('--min');
}
if ($this->simple) {
$pb->add('--simple');
}
$process = $pb->getProcess();
$returnCode = $process->run();
unlink($inputPath);
rmdir($inputDirPath);
if (127 === $returnCode) {
throw new \RuntimeException('Path to node executable could not be resolved.');
}
if (0 !== $returnCode) {
if (file_exists($outputPath)) {
unlink($outputPath);
}
throw FilterException::fromProcess($process)->setInput($asset->getContent());
}
if (!file_exists($outputPath)) {
throw new \RuntimeException('Error creating output file.');
}
$compiledJs = file_get_contents($outputPath);
unlink($outputPath);
$asset->setContent($compiledJs);
}
public function filterDump(AssetInterface $asset)
{
}
}
src/Assetic/Filter/BaseNodeFilter.php 0000666 00000001713 13436750441 0013527 0 ustar 00 nodePaths;
}
public function setNodePaths(array $nodePaths)
{
$this->nodePaths = $nodePaths;
}
public function addNodePath($nodePath)
{
$this->nodePaths[] = $nodePath;
}
protected function createProcessBuilder(array $arguments = array())
{
$pb = parent::createProcessBuilder($arguments);
if ($this->nodePaths) {
$this->mergeEnv($pb);
$pb->setEnv('NODE_PATH', implode(PATH_SEPARATOR, $this->nodePaths));
}
return $pb;
}
}
src/Assetic/Filter/CoffeeScriptFilter.php 0000666 00000003553 13436750441 0014427 0 ustar 00
*/
class CoffeeScriptFilter extends BaseNodeFilter
{
private $coffeeBin;
private $nodeBin;
// coffee options
private $bare;
private $noHeader;
public function __construct($coffeeBin = '/usr/bin/coffee', $nodeBin = null)
{
$this->coffeeBin = $coffeeBin;
$this->nodeBin = $nodeBin;
}
public function setBare($bare)
{
$this->bare = $bare;
}
public function setNoHeader($noHeader)
{
$this->noHeader = $noHeader;
}
public function filterLoad(AssetInterface $asset)
{
$input = FilesystemUtils::createTemporaryFile('coffee');
file_put_contents($input, $asset->getContent());
$pb = $this->createProcessBuilder($this->nodeBin
? array($this->nodeBin, $this->coffeeBin)
: array($this->coffeeBin));
$pb->add('-cp');
if ($this->bare) {
$pb->add('--bare');
}
if ($this->noHeader) {
$pb->add('--no-header');
}
$pb->add($input);
$proc = $pb->getProcess();
$code = $proc->run();
unlink($input);
if (0 !== $code) {
throw FilterException::fromProcess($proc)->setInput($asset->getContent());
}
$asset->setContent($proc->getOutput());
}
public function filterDump(AssetInterface $asset)
{
}
}
src/Assetic/Filter/PngoutFilter.php 0000666 00000006017 13436750441 0013325 0 ustar 00
*/
class PngoutFilter extends BaseProcessFilter
{
// -c#
const COLOR_GREY = '0';
const COLOR_RGB = '2';
const COLOR_PAL = '3';
const COLOR_GRAY_ALPHA = '4';
const COLOR_RGB_ALPHA = '6';
// -f#
const FILTER_NONE = '0';
const FILTER_X = '1';
const FILTER_Y = '2';
const FILTER_X_Y = '3';
const FILTER_PAETH = '4';
const FILTER_MIXED = '5';
// -s#
const STRATEGY_XTREME = '0';
const STRATEGY_INTENSE = '1';
const STRATEGY_LONGEST_MATCH = '2';
const STRATEGY_HUFFMAN_ONLY = '3';
const STRATEGY_UNCOMPRESSED = '4';
private $pngoutBin;
private $color;
private $filter;
private $strategy;
private $blockSplitThreshold;
/**
* Constructor.
*
* @param string $pngoutBin Path to the pngout binary
*/
public function __construct($pngoutBin = '/usr/bin/pngout')
{
$this->pngoutBin = $pngoutBin;
}
public function setColor($color)
{
$this->color = $color;
}
public function setFilter($filter)
{
$this->filter = $filter;
}
public function setStrategy($strategy)
{
$this->strategy = $strategy;
}
public function setBlockSplitThreshold($blockSplitThreshold)
{
$this->blockSplitThreshold = $blockSplitThreshold;
}
public function filterLoad(AssetInterface $asset)
{
}
public function filterDump(AssetInterface $asset)
{
$pb = $this->createProcessBuilder(array($this->pngoutBin));
if (null !== $this->color) {
$pb->add('-c'.$this->color);
}
if (null !== $this->filter) {
$pb->add('-f'.$this->filter);
}
if (null !== $this->strategy) {
$pb->add('-s'.$this->strategy);
}
if (null !== $this->blockSplitThreshold) {
$pb->add('-b'.$this->blockSplitThreshold);
}
$pb->add($input = FilesystemUtils::createTemporaryFile('pngout_in'));
file_put_contents($input, $asset->getContent());
$output = FilesystemUtils::createTemporaryFile('pngout_out');
unlink($output);
$pb->add($output .= '.png');
$proc = $pb->getProcess();
$code = $proc->run();
if (0 !== $code) {
unlink($input);
throw FilterException::fromProcess($proc)->setInput($asset->getContent());
}
$asset->setContent(file_get_contents($output));
unlink($input);
unlink($output);
}
}
src/Assetic/Filter/DependencyExtractorInterface.php 0000666 00000001566 13436750441 0016502 0 ustar 00
*/
interface DependencyExtractorInterface extends FilterInterface
{
/**
* Returns child assets.
*
* @param AssetFactory $factory The asset factory
* @param string $content The asset content
* @param string $loadPath An optional load path
*
* @return AssetInterface[] Child assets
*/
public function getChildren(AssetFactory $factory, $content, $loadPath = null);
}
src/Assetic/Filter/PackagerFilter.php 0000666 00000003124 13436750441 0013562 0 ustar 00
*/
class PackagerFilter implements FilterInterface
{
private $packages;
public function __construct(array $packages = array())
{
$this->packages = $packages;
}
public function addPackage($package)
{
$this->packages[] = $package;
}
public function filterLoad(AssetInterface $asset)
{
static $manifest = <<getContent());
$packager = new \Packager(array_merge(array($package), $this->packages));
$content = $packager->build(array(), array(), array('Application'.$hash));
unlink($package.'/package.yml');
unlink($package.'/source.js');
rmdir($package);
$asset->setContent($content);
}
public function filterDump(AssetInterface $asset)
{
}
}
src/Assetic/Filter/OptiPngFilter.php 0000666 00000003414 13436750441 0013427 0 ustar 00
*/
class OptiPngFilter extends BaseProcessFilter
{
private $optipngBin;
private $level;
/**
* Constructor.
*
* @param string $optipngBin Path to the optipng binary
*/
public function __construct($optipngBin = '/usr/bin/optipng')
{
$this->optipngBin = $optipngBin;
}
public function setLevel($level)
{
$this->level = $level;
}
public function filterLoad(AssetInterface $asset)
{
}
public function filterDump(AssetInterface $asset)
{
$pb = $this->createProcessBuilder(array($this->optipngBin));
if (null !== $this->level) {
$pb->add('-o')->add($this->level);
}
$pb->add('-out')->add($output = FilesystemUtils::createTemporaryFile('optipng_out'));
unlink($output);
$pb->add($input = FilesystemUtils::createTemporaryFile('optinpg_in'));
file_put_contents($input, $asset->getContent());
$proc = $pb->getProcess();
$code = $proc->run();
if (0 !== $code) {
unlink($input);
throw FilterException::fromProcess($proc)->setInput($asset->getContent());
}
$asset->setContent(file_get_contents($output));
unlink($input);
unlink($output);
}
}
src/Assetic/Filter/UglifyJsFilter.php 0000666 00000007601 13436750441 0013605 0 ustar 00
*/
class UglifyJsFilter extends BaseNodeFilter
{
private $uglifyjsBin;
private $nodeBin;
private $noCopyright;
private $beautify;
private $unsafe;
private $mangle;
private $defines;
/**
* @param string $uglifyjsBin Absolute path to the uglifyjs executable
* @param string $nodeBin Absolute path to the folder containg node.js executable
*/
public function __construct($uglifyjsBin = '/usr/bin/uglifyjs', $nodeBin = null)
{
$this->uglifyjsBin = $uglifyjsBin;
$this->nodeBin = $nodeBin;
}
/**
* Removes the first block of comments as well
* @param bool $noCopyright True to enable
*/
public function setNoCopyright($noCopyright)
{
$this->noCopyright = $noCopyright;
}
/**
* Output indented code
* @param bool $beautify True to enable
*/
public function setBeautify($beautify)
{
$this->beautify = $beautify;
}
/**
* Enable additional optimizations that are known to be unsafe in some situations.
* @param bool $unsafe True to enable
*/
public function setUnsafe($unsafe)
{
$this->unsafe = $unsafe;
}
/**
* Safely mangle variable and function names for greater file compress.
* @param bool $mangle True to enable
*/
public function setMangle($mangle)
{
$this->mangle = $mangle;
}
public function setDefines(array $defines)
{
$this->defines = $defines;
}
/**
* @see Assetic\Filter\FilterInterface::filterLoad()
*/
public function filterLoad(AssetInterface $asset)
{
}
/**
* Run the asset through UglifyJs
*
* @see Assetic\Filter\FilterInterface::filterDump()
*/
public function filterDump(AssetInterface $asset)
{
$pb = $this->createProcessBuilder(
$this->nodeBin
? array($this->nodeBin, $this->uglifyjsBin)
: array($this->uglifyjsBin)
);
if ($this->noCopyright) {
$pb->add('--no-copyright');
}
if ($this->beautify) {
$pb->add('--beautify');
}
if ($this->unsafe) {
$pb->add('--unsafe');
}
if (false === $this->mangle) {
$pb->add('--no-mangle');
}
if ($this->defines) {
foreach ($this->defines as $define) {
$pb->add('-d')->add($define);
}
}
// input and output files
$input = FilesystemUtils::createTemporaryFile('uglifyjs_in');
$output = FilesystemUtils::createTemporaryFile('uglifyjs_out');
file_put_contents($input, $asset->getContent());
$pb->add('-o')->add($output)->add($input);
$proc = $pb->getProcess();
$code = $proc->run();
unlink($input);
if (0 !== $code) {
if (file_exists($output)) {
unlink($output);
}
if (127 === $code) {
throw new \RuntimeException('Path to node executable could not be resolved.');
}
throw FilterException::fromProcess($proc)->setInput($asset->getContent());
}
if (!file_exists($output)) {
throw new \RuntimeException('Error creating output file.');
}
$uglifiedJs = file_get_contents($output);
unlink($output);
$asset->setContent($uglifiedJs);
}
}
src/Assetic/Filter/CleanCssFilter.php 0000666 00000021437 13436750441 0013547 0 ustar 00
*/
class CleanCssFilter extends BaseNodeFilter
{
private $cleanCssBin;
private $nodeBin;
private $keepLineBreaks;
private $compatibility;
private $debug;
private $rootPath;
private $skipImport = true;
private $timeout;
private $semanticMerging;
private $roundingPrecision;
private $removeSpecialComments;
private $onlyKeepFirstSpecialComment;
private $skipAdvanced;
private $skipAggresiveMerging;
private $skipImportFrom;
private $mediaMerging;
private $skipRebase;
private $skipRestructuring;
private $skipShorthandCompacting;
private $sourceMap;
private $sourceMapInlineSources;
/**
* @param string $cleanCssBin Absolute path to the cleancss executable
* @param string $nodeBin Absolute path to the folder containg node.js executable
*/
public function __construct($cleanCssBin = '/usr/bin/cleancss', $nodeBin = null)
{
$this->cleanCssBin = $cleanCssBin;
$this->nodeBin = $nodeBin;
}
/**
* Keep line breaks
* @param bool $keepLineBreaks True to enable
*/
public function setKeepLineBreaks($keepLineBreaks)
{
$this->keepLineBreaks = $keepLineBreaks;
}
/**
* Remove all special comments
* @param bool $removeSpecialComments True to enable
*/ // i.e. /*! comment */
public function setRemoveSpecialComments($removeSpecialComments)
{
$this->removeSpecialComments = $removeSpecialComments;
}
/**
* Remove all special comments except the first one
* @param bool $onlyKeepFirstSpecialComment True to enable
*/
public function setOnlyKeepFirstSpecialComment($onlyKeepFirstSpecialComment)
{
$this->onlyKeepFirstSpecialComment = $onlyKeepFirstSpecialComment;
}
/**
* Enables unsafe mode by assuming BEM-like semantic stylesheets (warning, this may break your styling!)
* @param bool $semanticMerging True to enable
*/
public function setSemanticMerging($semanticMerging)
{
$this->semanticMerging = $semanticMerging;
}
/**
* A root path to which resolve absolute @import rules
* @param string $rootPath
*/
public function setRootPath($rootPath)
{
$this->rootPath = $rootPath;
}
/**
* Disable @import processing
* @param bool $skipImport True to enable
*/
public function setSkipImport($skipImport)
{
$this->skipImport = $skipImport;
}
/**
* Per connection timeout when fetching remote @imports; defaults to 5 seconds
* @param int $timeout
*/
public function setTimeout($timeout)
{
$this->timeout = $timeout;
}
/**
* Disable URLs rebasing
* @param bool $skipRebase True to enable
*/
public function setSkipRebase($skipRebase)
{
$this->skipRebase = $skipRebase;
}
/**
* Disable restructuring optimizations
* @param bool $skipRestructuring True to enable
*/
public function setSkipRestructuring($skipRestructuring)
{
$this->skipRestructuring = $skipRestructuring;
}
/**
* Disable shorthand compacting
* @param bool $skipShorthandCompacting True to enable
*/
public function setSkipShorthandCompacting($skipShorthandCompacting)
{
$this->skipShorthandCompacting = $skipShorthandCompacting;
}
/**
* Enables building input's source map
* @param bool $sourceMap True to enable
*/
public function setSourceMap($sourceMap)
{
$this->sourceMap = $sourceMap;
}
/**
* Enables inlining sources inside source maps
* @param bool $sourceMapInlineSources True to enable
*/
public function setSourceMapInlineSources($sourceMapInlineSources)
{
$this->sourceMapInlineSources = $sourceMapInlineSources;
}
/**
* Disable advanced optimizations - selector & property merging, reduction, etc.
* @param bool $skipAdvanced True to enable
*/
public function setSkipAdvanced($skipAdvanced)
{
$this->skipAdvanced = $skipAdvanced;
}
/**
* Disable properties merging based on their order
* @param bool $skipAggresiveMerging True to enable
*/
public function setSkipAggresiveMerging($skipAggresiveMerging)
{
$this->skipAggresiveMerging = $skipAggresiveMerging;
}
/**
* Disable @import processing for specified rules
* @param string $skipImportFrom
*/
public function setSkipImportFrom($skipImportFrom)
{
$this->skipImportFrom = $skipImportFrom;
}
/**
* Disable @media merging
* @param bool $mediaMerging True to enable
*/
public function setMediaMerging($mediaMerging)
{
$this->mediaMerging = $mediaMerging;
}
/**
* Rounds to `N` decimal places. Defaults to 2. -1 disables rounding.
* @param int $roundingPrecision
*/
public function setRoundingPrecision($roundingPrecision)
{
$this->roundingPrecision = $roundingPrecision;
}
/**
* Force compatibility mode (see https://github.com/jakubpawlowicz/clean-css/blob/master/README.md#how-to-set-compatibility-mode for advanced examples)
* @param string $compatibility
*/
public function setCompatibility($compatibility)
{
$this->compatibility = $compatibility;
}
/**
* Shows debug information (minification time & compression efficiency)
* @param bool $debug True to enable
*/
public function setDebug($debug)
{
$this->debug = $debug;
}
/**
* @see Assetic\Filter\FilterInterface::filterLoad()
*/
public function filterLoad(AssetInterface $asset)
{
}
/**
* Run the asset through CleanCss
*
* @see Assetic\Filter\FilterInterface::filterDump()
*/
public function filterDump(AssetInterface $asset)
{
$pb = $this->createProcessBuilder($this->nodeBin
? array($this->nodeBin, $this->cleanCssBin)
: array($this->cleanCssBin));
if ($this->keepLineBreaks) {
$pb->add('--keep-line-breaks');
}
if ($this->compatibility) {
$pb->add('--compatibility ' .$this->compatibility);
}
if ($this->debug) {
$pb->add('--debug');
}
if ($this->rootPath) {
$pb->add('--root ' .$this->rootPath);
}
if ($this->skipImport) {
$pb->add('--skip-import');
}
if ($this->timeout) {
$pb->add('--timeout ' .$this->timeout);
}
if ($this->roundingPrecision) {
$pb->add('--rounding-precision ' .$this->roundingPrecision);
}
if ($this->removeSpecialComments) {
$pb->add('--s0');
}
if ($this->onlyKeepFirstSpecialComment) {
$pb->add('--s1');
}
if ($this->semanticMerging) {
$pb->add('--semantic-merging');
}
if ($this->skipAdvanced) {
$pb->add('--skip-advanced');
}
if ($this->skipAggresiveMerging) {
$pb->add('--skip-aggressive-merging');
}
if ($this->skipImportFrom) {
$pb->add('--skip-import-from ' .$this->skipImportFrom);
}
if ($this->mediaMerging) {
$pb->add('--skip-media-merging');
}
if ($this->skipRebase) {
$pb->add('--skip-rebase');
}
if ($this->skipRestructuring) {
$pb->add('--skip-restructuring');
}
if ($this->skipShorthandCompacting) {
$pb->add('--skip-shorthand-compacting');
}
if ($this->sourceMap) {
$pb->add('--source-map');
}
if ($this->sourceMapInlineSources) {
$pb->add('--source-map-inline-sources');
}
// input and output files
$input = tempnam(sys_get_temp_dir(), 'input');
file_put_contents($input, $asset->getContent());
$pb->add($input);
$proc = $pb->getProcess();
$code = $proc->run();
unlink($input);
if (127 === $code) {
throw new \RuntimeException('Path to node executable could not be resolved.');
}
if (0 !== $code) {
throw FilterException::fromProcess($proc)->setInput($asset->getContent());
}
$asset->setContent($proc->getOutput());
}
}
src/Assetic/Filter/CssMinFilter.php 0000666 00000003156 13436750441 0013246 0 ustar 00
*/
class CssMinFilter implements FilterInterface
{
private $filters;
private $plugins;
public function __construct()
{
$this->filters = array();
$this->plugins = array();
}
public function setFilters(array $filters)
{
$this->filters = $filters;
}
public function setFilter($name, $value)
{
$this->filters[$name] = $value;
}
public function setPlugins(array $plugins)
{
$this->plugins = $plugins;
}
public function setPlugin($name, $value)
{
$this->plugins[$name] = $value;
}
public function filterLoad(AssetInterface $asset)
{
}
public function filterDump(AssetInterface $asset)
{
$filters = $this->filters;
$plugins = $this->plugins;
if (isset($filters['ImportImports']) && true === $filters['ImportImports']) {
if ($dir = $asset->getSourceDirectory()) {
$filters['ImportImports'] = array('BasePath' => $dir);
} else {
unset($filters['ImportImports']);
}
}
$asset->setContent(\CssMin::minify($asset->getContent(), $filters, $plugins));
}
}
src/Assetic/Filter/Sass/ScssFilter.php 0000666 00000001143 13436750441 0013670 0 ustar 00
*/
class ScssFilter extends SassFilter
{
public function __construct($sassPath = '/usr/bin/sass', $rubyPath = null)
{
parent::__construct($sassPath, $rubyPath);
$this->setScss(true);
}
}
src/Assetic/Filter/Sass/SassFilter.php 0000666 00000010404 13436750441 0013666 0 ustar 00
*/
class SassFilter extends BaseSassFilter
{
const STYLE_NESTED = 'nested';
const STYLE_EXPANDED = 'expanded';
const STYLE_COMPACT = 'compact';
const STYLE_COMPRESSED = 'compressed';
private $sassPath;
private $rubyPath;
private $unixNewlines;
private $scss;
private $style;
private $precision;
private $quiet;
private $debugInfo;
private $lineNumbers;
private $sourceMap;
private $cacheLocation;
private $noCache;
private $compass;
public function __construct($sassPath = '/usr/bin/sass', $rubyPath = null)
{
$this->sassPath = $sassPath;
$this->rubyPath = $rubyPath;
$this->cacheLocation = FilesystemUtils::getTemporaryDirectory();
}
public function setUnixNewlines($unixNewlines)
{
$this->unixNewlines = $unixNewlines;
}
public function setScss($scss)
{
$this->scss = $scss;
}
public function setStyle($style)
{
$this->style = $style;
}
public function setPrecision($precision)
{
$this->precision = $precision;
}
public function setQuiet($quiet)
{
$this->quiet = $quiet;
}
public function setDebugInfo($debugInfo)
{
$this->debugInfo = $debugInfo;
}
public function setLineNumbers($lineNumbers)
{
$this->lineNumbers = $lineNumbers;
}
public function setSourceMap($sourceMap)
{
$this->sourceMap = $sourceMap;
}
public function setCacheLocation($cacheLocation)
{
$this->cacheLocation = $cacheLocation;
}
public function setNoCache($noCache)
{
$this->noCache = $noCache;
}
public function setCompass($compass)
{
$this->compass = $compass;
}
public function filterLoad(AssetInterface $asset)
{
$sassProcessArgs = array($this->sassPath);
if (null !== $this->rubyPath) {
$sassProcessArgs = array_merge(explode(' ', $this->rubyPath), $sassProcessArgs);
}
$pb = $this->createProcessBuilder($sassProcessArgs);
if ($dir = $asset->getSourceDirectory()) {
$pb->add('--load-path')->add($dir);
}
if ($this->unixNewlines) {
$pb->add('--unix-newlines');
}
if (true === $this->scss || (null === $this->scss && 'scss' == pathinfo($asset->getSourcePath(), PATHINFO_EXTENSION))) {
$pb->add('--scss');
}
if ($this->style) {
$pb->add('--style')->add($this->style);
}
if ($this->precision) {
$pb->add('--precision')->add($this->precision);
}
if ($this->quiet) {
$pb->add('--quiet');
}
if ($this->debugInfo) {
$pb->add('--debug-info');
}
if ($this->lineNumbers) {
$pb->add('--line-numbers');
}
if ($this->sourceMap) {
$pb->add('--sourcemap');
}
foreach ($this->loadPaths as $loadPath) {
$pb->add('--load-path')->add($loadPath);
}
if ($this->cacheLocation) {
$pb->add('--cache-location')->add($this->cacheLocation);
}
if ($this->noCache) {
$pb->add('--no-cache');
}
if ($this->compass) {
$pb->add('--compass');
}
// input
$pb->add($input = FilesystemUtils::createTemporaryFile('sass'));
file_put_contents($input, $asset->getContent());
$proc = $pb->getProcess();
$code = $proc->run();
unlink($input);
if (0 !== $code) {
throw FilterException::fromProcess($proc)->setInput($asset->getContent());
}
$asset->setContent($proc->getOutput());
}
public function filterDump(AssetInterface $asset)
{
}
}
src/Assetic/Filter/Sass/BaseSassFilter.php 0000666 00000005363 13436750441 0014471 0 ustar 00 loadPaths = $loadPaths;
}
public function addLoadPath($loadPath)
{
$this->loadPaths[] = $loadPath;
}
public function getChildren(AssetFactory $factory, $content, $loadPath = null)
{
$loadPaths = $this->loadPaths;
if ($loadPath) {
array_unshift($loadPaths, $loadPath);
}
if (!$loadPaths) {
return array();
}
$children = array();
foreach (CssUtils::extractImports($content) as $reference) {
if ('.css' === substr($reference, -4)) {
// skip normal css imports
// todo: skip imports with media queries
continue;
}
// the reference may or may not have an extension or be a partial
if (pathinfo($reference, PATHINFO_EXTENSION)) {
$needles = array(
$reference,
self::partialize($reference),
);
} else {
$needles = array(
$reference.'.scss',
$reference.'.sass',
self::partialize($reference).'.scss',
self::partialize($reference).'.sass',
);
}
foreach ($loadPaths as $loadPath) {
foreach ($needles as $needle) {
if (file_exists($file = $loadPath.'/'.$needle)) {
$coll = $factory->createAsset($file, array(), array('root' => $loadPath));
foreach ($coll as $leaf) {
/** @var $leaf AssetInterface */
$leaf->ensureFilter($this);
$children[] = $leaf;
goto next_reference;
}
}
}
}
next_reference:
}
return $children;
}
private static function partialize($reference)
{
$parts = pathinfo($reference);
if ('.' === $parts['dirname']) {
$partial = '_'.$parts['filename'];
} else {
$partial = $parts['dirname'].DIRECTORY_SEPARATOR.'_'.$parts['filename'];
}
if (isset($parts['extension'])) {
$partial .= '.'.$parts['extension'];
}
return $partial;
}
}
src/Assetic/Filter/LessFilter.php 0000666 00000011740 13436750441 0012756 0 ustar 00
*/
class LessFilter extends BaseNodeFilter implements DependencyExtractorInterface
{
private $nodeBin;
/**
* @var array
*/
private $treeOptions;
/**
* @var array
*/
private $parserOptions;
/**
* Load Paths
*
* A list of paths which less will search for includes.
*
* @var array
*/
protected $loadPaths = array();
/**
* Constructor.
*
* @param string $nodeBin The path to the node binary
* @param array $nodePaths An array of node paths
*/
public function __construct($nodeBin = '/usr/bin/node', array $nodePaths = array())
{
$this->nodeBin = $nodeBin;
$this->setNodePaths($nodePaths);
$this->treeOptions = array();
$this->parserOptions = array();
}
/**
* @param bool $compress
*/
public function setCompress($compress)
{
$this->addTreeOption('compress', $compress);
}
public function setLoadPaths(array $loadPaths)
{
$this->loadPaths = $loadPaths;
}
/**
* Adds a path where less will search for includes
*
* @param string $path Load path (absolute)
*/
public function addLoadPath($path)
{
$this->loadPaths[] = $path;
}
/**
* @param string $code
* @param string $value
*/
public function addTreeOption($code, $value)
{
$this->treeOptions[$code] = $value;
}
/**
* @param string $code
* @param string $value
*/
public function addParserOption($code, $value)
{
$this->parserOptions[$code] = $value;
}
public function filterLoad(AssetInterface $asset)
{
static $format = <<<'EOF'
var less = require('less');
var sys = require(process.binding('natives').util ? 'util' : 'sys');
less.render(%s, %s, function(error, css) {
if (error) {
less.writeError(error);
process.exit(2);
}
try {
if (typeof css == 'string') {
sys.print(css);
} else {
sys.print(css.css);
}
} catch (e) {
less.writeError(error);
process.exit(3);
}
});
EOF;
// parser options
$parserOptions = $this->parserOptions;
if ($dir = $asset->getSourceDirectory()) {
$parserOptions['paths'] = array($dir);
$parserOptions['filename'] = basename($asset->getSourcePath());
}
foreach ($this->loadPaths as $loadPath) {
$parserOptions['paths'][] = $loadPath;
}
$pb = $this->createProcessBuilder();
$pb->add($this->nodeBin)->add($input = FilesystemUtils::createTemporaryFile('less'));
file_put_contents($input, sprintf($format,
json_encode($asset->getContent()),
json_encode(array_merge($parserOptions, $this->treeOptions))
));
$proc = $pb->getProcess();
$code = $proc->run();
unlink($input);
if (0 !== $code) {
throw FilterException::fromProcess($proc)->setInput($asset->getContent());
}
$asset->setContent($proc->getOutput());
}
public function filterDump(AssetInterface $asset)
{
}
/**
* @todo support for import-once
* @todo support for import (less) "lib.css"
*/
public function getChildren(AssetFactory $factory, $content, $loadPath = null)
{
$loadPaths = $this->loadPaths;
if (null !== $loadPath) {
$loadPaths[] = $loadPath;
}
if (empty($loadPaths)) {
return array();
}
$children = array();
foreach (LessUtils::extractImports($content) as $reference) {
if ('.css' === substr($reference, -4)) {
// skip normal css imports
// todo: skip imports with media queries
continue;
}
if ('.less' !== substr($reference, -5)) {
$reference .= '.less';
}
foreach ($loadPaths as $loadPath) {
if (file_exists($file = $loadPath.'/'.$reference)) {
$coll = $factory->createAsset($file, array(), array('root' => $loadPath));
foreach ($coll as $leaf) {
$leaf->ensureFilter($this);
$children[] = $leaf;
goto next_reference;
}
}
}
next_reference:
}
return $children;
}
}
src/Assetic/Filter/JSqueezeFilter.php 0000666 00000003747 13436750441 0013613 0 ustar 00
*/
class JSqueezeFilter implements FilterInterface
{
private $singleLine = true;
private $keepImportantComments = true;
private $className;
private $specialVarRx = false;
private $defaultRx;
public function __construct()
{
// JSqueeze is namespaced since 2.x, this works with both 1.x and 2.x
if (class_exists('\\Patchwork\\JSqueeze')) {
$this->className = '\\Patchwork\\JSqueeze';
$this->defaultRx = \Patchwork\JSqueeze::SPECIAL_VAR_PACKER;
} else {
$this->className = '\\JSqueeze';
$this->defaultRx = \JSqueeze::SPECIAL_VAR_RX;
}
}
public function setSingleLine($bool)
{
$this->singleLine = (bool) $bool;
}
// call setSpecialVarRx(true) to enable global var/method/property
// renaming with the default regex (for 1.x or 2.x)
public function setSpecialVarRx($specialVarRx)
{
if (true === $specialVarRx) {
$this->specialVarRx = $this->defaultRx;
} else {
$this->specialVarRx = $specialVarRx;
}
}
public function keepImportantComments($bool)
{
$this->keepImportantComments = (bool) $bool;
}
public function filterLoad(AssetInterface $asset)
{
}
public function filterDump(AssetInterface $asset)
{
$parser = new $this->className();
$asset->setContent($parser->squeeze(
$asset->getContent(),
$this->singleLine,
$this->keepImportantComments,
$this->specialVarRx
));
}
}
src/Assetic/AssetWriter.php 0000666 00000004771 13436750441 0011737 0 ustar 00
* @author Johannes M. Schmitt
*/
class AssetWriter
{
private $dir;
private $values;
/**
* Constructor.
*
* @param string $dir The base web directory
* @param array $values Variable values
*
* @throws \InvalidArgumentException if a variable value is not a string
*/
public function __construct($dir, array $values = array())
{
foreach ($values as $var => $vals) {
foreach ($vals as $value) {
if (!is_string($value)) {
throw new \InvalidArgumentException(sprintf('All variable values must be strings, but got %s for variable "%s".', json_encode($value), $var));
}
}
}
$this->dir = $dir;
$this->values = $values;
}
public function writeManagerAssets(AssetManager $am)
{
foreach ($am->getNames() as $name) {
$this->writeAsset($am->get($name));
}
}
public function writeAsset(AssetInterface $asset)
{
foreach (VarUtils::getCombinations($asset->getVars(), $this->values) as $combination) {
$asset->setValues($combination);
static::write(
$this->dir.'/'.VarUtils::resolve(
$asset->getTargetPath(),
$asset->getVars(),
$asset->getValues()
),
$asset->dump()
);
}
}
protected static function write($path, $contents)
{
if (!is_dir($dir = dirname($path)) && false === @mkdir($dir, 0777, true)) {
throw new \RuntimeException('Unable to create directory '.$dir);
}
if (false === @file_put_contents($path, $contents)) {
throw new \RuntimeException('Unable to write file '.$path);
}
}
/**
* Not used.
*
* This method is provided for backward compatibility with certain versions
* of AsseticBundle.
*/
private function getCombinations(array $vars)
{
return VarUtils::getCombinations($vars, $this->values);
}
}
src/Assetic/Util/TraversableString.php 0000666 00000001556 13436750441 0014037 0 ustar 00
*/
class TraversableString implements \IteratorAggregate, \Countable
{
private $one;
private $many;
public function __construct($one, array $many)
{
$this->one = $one;
$this->many = $many;
}
public function getIterator()
{
return new \ArrayIterator($this->many);
}
public function count()
{
return count($this->many);
}
public function __toString()
{
return (string) $this->one;
}
}
src/Assetic/Util/CssUtils.php 0000666 00000010071 13436750441 0012137 0 ustar 00
*/
abstract class CssUtils
{
const REGEX_URLS = '/url\((["\']?)(?P.*?)(\\1)\)/';
const REGEX_IMPORTS = '/@import (?:url\()?(\'|"|)(?P[^\'"\)\n\r]*)\1\)?;?/';
const REGEX_IMPORTS_NO_URLS = '/@import (?!url\()(\'|"|)(?P[^\'"\)\n\r]*)\1;?/';
const REGEX_IE_FILTERS = '/src=(["\']?)(?P.*?)\\1/';
const REGEX_COMMENTS = '/(\/\*[^*]*\*+(?:[^\/][^*]*\*+)*\/)/';
/**
* Filters all references -- url() and "@import" -- through a callable.
*
* @param string $content The CSS
* @param callable $callback A PHP callable
*
* @return string The filtered CSS
*/
public static function filterReferences($content, $callback)
{
$content = static::filterUrls($content, $callback);
$content = static::filterImports($content, $callback, false);
$content = static::filterIEFilters($content, $callback);
return $content;
}
/**
* Filters all CSS url()'s through a callable.
*
* @param string $content The CSS
* @param callable $callback A PHP callable
*
* @return string The filtered CSS
*/
public static function filterUrls($content, $callback)
{
$pattern = static::REGEX_URLS;
return static::filterCommentless($content, function ($part) use (& $callback, $pattern) {
return preg_replace_callback($pattern, $callback, $part);
});
}
/**
* Filters all CSS imports through a callable.
*
* @param string $content The CSS
* @param callable $callback A PHP callable
* @param Boolean $includeUrl Whether to include url() in the pattern
*
* @return string The filtered CSS
*/
public static function filterImports($content, $callback, $includeUrl = true)
{
$pattern = $includeUrl ? static::REGEX_IMPORTS : static::REGEX_IMPORTS_NO_URLS;
return static::filterCommentless($content, function ($part) use (& $callback, $pattern) {
return preg_replace_callback($pattern, $callback, $part);
});
}
/**
* Filters all IE filters (AlphaImageLoader filter) through a callable.
*
* @param string $content The CSS
* @param callable $callback A PHP callable
*
* @return string The filtered CSS
*/
public static function filterIEFilters($content, $callback)
{
$pattern = static::REGEX_IE_FILTERS;
return static::filterCommentless($content, function ($part) use (& $callback, $pattern) {
return preg_replace_callback($pattern, $callback, $part);
});
}
/**
* Filters each non-comment part through a callable.
*
* @param string $content The CSS
* @param callable $callback A PHP callable
*
* @return string The filtered CSS
*/
public static function filterCommentless($content, $callback)
{
$result = '';
foreach (preg_split(static::REGEX_COMMENTS, $content, -1, PREG_SPLIT_DELIM_CAPTURE) as $part) {
if (!preg_match(static::REGEX_COMMENTS, $part, $match) || $part != $match[0]) {
$part = call_user_func($callback, $part);
}
$result .= $part;
}
return $result;
}
/**
* Extracts all references from the supplied CSS content.
*
* @param string $content The CSS content
*
* @return array An array of unique URLs
*/
public static function extractImports($content)
{
$imports = array();
static::filterImports($content, function ($matches) use (&$imports) {
$imports[] = $matches['url'];
});
return array_unique($imports);
}
final private function __construct()
{
}
}
src/Assetic/Util/VarUtils.php 0000666 00000004141 13436750441 0012140 0 ustar 00
*/
abstract class VarUtils
{
/**
* Resolves variable placeholders.
*
* @param string $template A template string
* @param array $vars Variable names
* @param array $values Variable values
*
* @return string The resolved string
*
* @throws \InvalidArgumentException If there is a variable with no value
*/
public static function resolve($template, array $vars, array $values)
{
$map = array();
foreach ($vars as $var) {
if (false === strpos($template, '{'.$var.'}')) {
continue;
}
if (!isset($values[$var])) {
throw new \InvalidArgumentException(sprintf('The template "%s" contains the variable "%s", but was not given any value for it.', $template, $var));
}
$map['{'.$var.'}'] = $values[$var];
}
return strtr($template, $map);
}
public static function getCombinations(array $vars, array $values)
{
if (!$vars) {
return array(array());
}
$combinations = array();
$nbValues = array();
foreach ($values as $var => $vals) {
if (!in_array($var, $vars, true)) {
continue;
}
$nbValues[$var] = count($vals);
}
for ($i = array_product($nbValues), $c = $i * 2; $i < $c; $i++) {
$k = $i;
$combination = array();
foreach ($vars as $var) {
$combination[$var] = $values[$var][$k % $nbValues[$var]];
$k = intval($k / $nbValues[$var]);
}
$combinations[] = $combination;
}
return $combinations;
}
final private function __construct()
{
}
}
src/Assetic/Util/FilesystemUtils.php 0000666 00000004251 13436750441 0013536 0 ustar 00
*/
class FilesystemUtils
{
/**
* Recursively removes a directory from the filesystem.
*/
public static function removeDirectory($directory)
{
$inner = new \RecursiveDirectoryIterator($directory, \FilesystemIterator::SKIP_DOTS);
$outer = new \RecursiveIteratorIterator($inner, \RecursiveIteratorIterator::SELF_FIRST);
// remove the files first
foreach ($outer as $file) {
if ($file->isFile()) {
unlink($file);
}
}
// remove the sub-directories next
$files = iterator_to_array($outer);
foreach (array_reverse($files) as $file) {
/** @var \SplFileInfo $file */
if ($file->isDir()) {
rmdir($file);
}
}
// finally the directory itself
rmdir($directory);
}
/**
* Creates a throw-away directory.
*
* This is not considered a "temporary" directory because it will not be
* automatically deleted at the end of the request or process. It must be
* deleted manually.
*
* @param string $prefix A prefix for the directory name
*
* @return string The directory path
*/
public static function createThrowAwayDirectory($prefix)
{
$directory = self::getTemporaryDirectory().DIRECTORY_SEPARATOR.uniqid('assetic_'.$prefix);
mkdir($directory);
return $directory;
}
/**
* Creates a temporary file.
*
* @param string $prefix A prefix for the file name
*
* @return string The file path
*/
public static function createTemporaryFile($prefix)
{
return tempnam(self::getTemporaryDirectory(), 'assetic_'.$prefix);
}
public static function getTemporaryDirectory()
{
return realpath(sys_get_temp_dir());
}
}
src/Assetic/Util/LessUtils.php 0000666 00000001325 13436750441 0012317 0 ustar 00
*/
abstract class LessUtils extends CssUtils
{
const REGEX_IMPORTS = '/@import(?:-once)? (?:\([a-z]*\) )?(?:url\()?(\'|"|)(?P[^\'"\)\n\r]*)\1\)?;?/';
const REGEX_IMPORTS_NO_URLS = '/@import(?:-once)? (?:\([a-z]*\) )?(?!url\()(\'|"|)(?P[^\'"\)\n\r]*)\1;?/';
const REGEX_COMMENTS = '/((?:\/\*[^*]*\*+(?:[^\/][^*]*\*+)*\/)|\/\/[^\n]+)/';
}
src/Assetic/FilterManager.php 0000666 00000002616 13436750441 0012177 0 ustar 00
*/
class FilterManager
{
private $filters = array();
public function set($alias, FilterInterface $filter)
{
$this->checkName($alias);
$this->filters[$alias] = $filter;
}
public function get($alias)
{
if (!isset($this->filters[$alias])) {
throw new \InvalidArgumentException(sprintf('There is no "%s" filter.', $alias));
}
return $this->filters[$alias];
}
public function has($alias)
{
return isset($this->filters[$alias]);
}
public function getNames()
{
return array_keys($this->filters);
}
/**
* Checks that a name is valid.
*
* @param string $name An asset name candidate
*
* @throws \InvalidArgumentException If the asset name is invalid
*/
protected function checkName($name)
{
if (!ctype_alnum(str_replace('_', '', $name))) {
throw new \InvalidArgumentException(sprintf('The name "%s" is invalid.', $name));
}
}
}
src/Assetic/Asset/AssetInterface.php 0000666 00000010236 13436750441 0013433 0 ustar 00
*/
interface AssetInterface
{
/**
* Ensures the current asset includes the supplied filter.
*
* @param FilterInterface $filter A filter
*/
public function ensureFilter(FilterInterface $filter);
/**
* Returns an array of filters currently applied.
*
* @return array An array of filters
*/
public function getFilters();
/**
* Clears all filters from the current asset.
*/
public function clearFilters();
/**
* Loads the asset into memory and applies load filters.
*
* You may provide an additional filter to apply during load.
*
* @param FilterInterface $additionalFilter An additional filter
*/
public function load(FilterInterface $additionalFilter = null);
/**
* Applies dump filters and returns the asset as a string.
*
* You may provide an additional filter to apply during dump.
*
* Dumping an asset should not change its state.
*
* If the current asset has not been loaded yet, it should be
* automatically loaded at this time.
*
* @param FilterInterface $additionalFilter An additional filter
*
* @return string The filtered content of the current asset
*/
public function dump(FilterInterface $additionalFilter = null);
/**
* Returns the loaded content of the current asset.
*
* @return string The content
*/
public function getContent();
/**
* Sets the content of the current asset.
*
* Filters can use this method to change the content of the asset.
*
* @param string $content The asset content
*/
public function setContent($content);
/**
* Returns an absolute path or URL to the source asset's root directory.
*
* This value should be an absolute path to a directory in the filesystem,
* an absolute URL with no path, or null.
*
* For example:
*
* * '/path/to/web'
* * 'http://example.com'
* * null
*
* @return string|null The asset's root
*/
public function getSourceRoot();
/**
* Returns the relative path for the source asset.
*
* This value can be combined with the asset's source root (if both are
* non-null) to get something compatible with file_get_contents().
*
* For example:
*
* * 'js/main.js'
* * 'main.js'
* * null
*
* @return string|null The source asset path
*/
public function getSourcePath();
/**
* Returns the asset's source directory.
*
* The source directory is the directory the asset was located in
* and can be used to resolve references relative to an asset.
*
* @return string|null The asset's source directory
*/
public function getSourceDirectory();
/**
* Returns the URL for the current asset.
*
* @return string|null A web URL where the asset will be dumped
*/
public function getTargetPath();
/**
* Sets the URL for the current asset.
*
* @param string $targetPath A web URL where the asset will be dumped
*/
public function setTargetPath($targetPath);
/**
* Returns the time the current asset was last modified.
*
* @return integer|null A UNIX timestamp
*/
public function getLastModified();
/**
* Returns an array of variable names for this asset.
*
* @return array
*/
public function getVars();
/**
* Sets the values for the asset's variables.
*
* @param array $values
*/
public function setValues(array $values);
/**
* Returns the current values for this asset.
*
* @return array an array of strings
*/
public function getValues();
}
src/Assetic/Asset/AssetCollectionInterface.php 0000666 00000003260 13436750441 0015446 0 ustar 00
*/
interface AssetCollectionInterface extends AssetInterface, \Traversable
{
/**
* Returns all child assets.
*
* @return array An array of AssetInterface objects
*/
public function all();
/**
* Adds an asset to the current collection.
*
* @param AssetInterface $asset An asset
*/
public function add(AssetInterface $asset);
/**
* Removes a leaf.
*
* @param AssetInterface $leaf The leaf to remove
* @param Boolean $graceful Whether the failure should return false or throw an exception
*
* @return Boolean Whether the asset has been found
*
* @throws \InvalidArgumentException If the asset cannot be found
*/
public function removeLeaf(AssetInterface $leaf, $graceful = false);
/**
* Replaces an existing leaf with a new one.
*
* @param AssetInterface $needle The current asset to replace
* @param AssetInterface $replacement The new asset
* @param Boolean $graceful Whether the failure should return false or throw an exception
*
* @return Boolean Whether the asset has been found
*
* @throws \InvalidArgumentException If the asset cannot be found
*/
public function replaceLeaf(AssetInterface $needle, AssetInterface $replacement, $graceful = false);
}
src/Assetic/Asset/AssetCollection.php 0000666 00000013411 13436750441 0013624 0 ustar 00
*/
class AssetCollection implements \IteratorAggregate, AssetCollectionInterface
{
private $assets;
private $filters;
private $sourceRoot;
private $targetPath;
private $content;
private $clones;
private $vars;
private $values;
/**
* Constructor.
*
* @param array $assets Assets for the current collection
* @param array $filters Filters for the current collection
* @param string $sourceRoot The root directory
* @param array $vars
*/
public function __construct($assets = array(), $filters = array(), $sourceRoot = null, array $vars = array())
{
$this->assets = array();
foreach ($assets as $asset) {
$this->add($asset);
}
$this->filters = new FilterCollection($filters);
$this->sourceRoot = $sourceRoot;
$this->clones = new \SplObjectStorage();
$this->vars = $vars;
$this->values = array();
}
public function __clone()
{
$this->filters = clone $this->filters;
$this->clones = new \SplObjectStorage();
}
public function all()
{
return $this->assets;
}
public function add(AssetInterface $asset)
{
$this->assets[] = $asset;
}
public function removeLeaf(AssetInterface $needle, $graceful = false)
{
foreach ($this->assets as $i => $asset) {
$clone = isset($this->clones[$asset]) ? $this->clones[$asset] : null;
if (in_array($needle, array($asset, $clone), true)) {
unset($this->clones[$asset], $this->assets[$i]);
return true;
}
if ($asset instanceof AssetCollectionInterface && $asset->removeLeaf($needle, true)) {
return true;
}
}
if ($graceful) {
return false;
}
throw new \InvalidArgumentException('Leaf not found.');
}
public function replaceLeaf(AssetInterface $needle, AssetInterface $replacement, $graceful = false)
{
foreach ($this->assets as $i => $asset) {
$clone = isset($this->clones[$asset]) ? $this->clones[$asset] : null;
if (in_array($needle, array($asset, $clone), true)) {
unset($this->clones[$asset]);
$this->assets[$i] = $replacement;
return true;
}
if ($asset instanceof AssetCollectionInterface && $asset->replaceLeaf($needle, $replacement, true)) {
return true;
}
}
if ($graceful) {
return false;
}
throw new \InvalidArgumentException('Leaf not found.');
}
public function ensureFilter(FilterInterface $filter)
{
$this->filters->ensure($filter);
}
public function getFilters()
{
return $this->filters->all();
}
public function clearFilters()
{
$this->filters->clear();
$this->clones = new \SplObjectStorage();
}
public function load(FilterInterface $additionalFilter = null)
{
// loop through leaves and load each asset
$parts = array();
foreach ($this as $asset) {
$asset->load($additionalFilter);
$parts[] = $asset->getContent();
}
$this->content = implode("\n", $parts);
}
public function dump(FilterInterface $additionalFilter = null)
{
// loop through leaves and dump each asset
$parts = array();
foreach ($this as $asset) {
$parts[] = $asset->dump($additionalFilter);
}
return implode("\n", $parts);
}
public function getContent()
{
return $this->content;
}
public function setContent($content)
{
$this->content = $content;
}
public function getSourceRoot()
{
return $this->sourceRoot;
}
public function getSourcePath()
{
}
public function getSourceDirectory()
{
}
public function getTargetPath()
{
return $this->targetPath;
}
public function setTargetPath($targetPath)
{
$this->targetPath = $targetPath;
}
/**
* Returns the highest last-modified value of all assets in the current collection.
*
* @return integer|null A UNIX timestamp
*/
public function getLastModified()
{
if (!count($this->assets)) {
return;
}
$mtime = 0;
foreach ($this as $asset) {
$assetMtime = $asset->getLastModified();
if ($assetMtime > $mtime) {
$mtime = $assetMtime;
}
}
return $mtime;
}
/**
* Returns an iterator for looping recursively over unique leaves.
*/
public function getIterator()
{
return new \RecursiveIteratorIterator(new AssetCollectionFilterIterator(new AssetCollectionIterator($this, $this->clones)));
}
public function getVars()
{
return $this->vars;
}
public function setValues(array $values)
{
$this->values = $values;
foreach ($this as $asset) {
$asset->setValues(array_intersect_key($values, array_flip($asset->getVars())));
}
}
public function getValues()
{
return $this->values;
}
}
src/Assetic/Asset/HttpAsset.php 0000666 00000004546 13436750441 0012461 0 ustar 00
*/
class HttpAsset extends BaseAsset
{
private $sourceUrl;
private $ignoreErrors;
/**
* Constructor.
*
* @param string $sourceUrl The source URL
* @param array $filters An array of filters
* @param Boolean $ignoreErrors
* @param array $vars
*
* @throws \InvalidArgumentException If the first argument is not an URL
*/
public function __construct($sourceUrl, $filters = array(), $ignoreErrors = false, array $vars = array())
{
if (0 === strpos($sourceUrl, '//')) {
$sourceUrl = 'http:'.$sourceUrl;
} elseif (false === strpos($sourceUrl, '://')) {
throw new \InvalidArgumentException(sprintf('"%s" is not a valid URL.', $sourceUrl));
}
$this->sourceUrl = $sourceUrl;
$this->ignoreErrors = $ignoreErrors;
list($scheme, $url) = explode('://', $sourceUrl, 2);
list($host, $path) = explode('/', $url, 2);
parent::__construct($filters, $scheme.'://'.$host, $path, $vars);
}
public function load(FilterInterface $additionalFilter = null)
{
$content = @file_get_contents(
VarUtils::resolve($this->sourceUrl, $this->getVars(), $this->getValues())
);
if (false === $content && !$this->ignoreErrors) {
throw new \RuntimeException(sprintf('Unable to load asset from URL "%s"', $this->sourceUrl));
}
$this->doLoad($content, $additionalFilter);
}
public function getLastModified()
{
if (false !== @file_get_contents($this->sourceUrl, false, stream_context_create(array('http' => array('method' => 'HEAD'))))) {
foreach ($http_response_header as $header) {
if (0 === stripos($header, 'Last-Modified: ')) {
list(, $mtime) = explode(':', $header, 2);
return strtotime(trim($mtime));
}
}
}
}
}
src/Assetic/Asset/AssetCache.php 0000666 00000010325 13436750441 0012535 0 ustar 00
*/
class AssetCache implements AssetInterface
{
private $asset;
private $cache;
public function __construct(AssetInterface $asset, CacheInterface $cache)
{
$this->asset = $asset;
$this->cache = $cache;
}
public function ensureFilter(FilterInterface $filter)
{
$this->asset->ensureFilter($filter);
}
public function getFilters()
{
return $this->asset->getFilters();
}
public function clearFilters()
{
$this->asset->clearFilters();
}
public function load(FilterInterface $additionalFilter = null)
{
$cacheKey = self::getCacheKey($this->asset, $additionalFilter, 'load');
if ($this->cache->has($cacheKey)) {
$this->asset->setContent($this->cache->get($cacheKey));
return;
}
$this->asset->load($additionalFilter);
$this->cache->set($cacheKey, $this->asset->getContent());
}
public function dump(FilterInterface $additionalFilter = null)
{
$cacheKey = self::getCacheKey($this->asset, $additionalFilter, 'dump');
if ($this->cache->has($cacheKey)) {
return $this->cache->get($cacheKey);
}
$content = $this->asset->dump($additionalFilter);
$this->cache->set($cacheKey, $content);
return $content;
}
public function getContent()
{
return $this->asset->getContent();
}
public function setContent($content)
{
$this->asset->setContent($content);
}
public function getSourceRoot()
{
return $this->asset->getSourceRoot();
}
public function getSourcePath()
{
return $this->asset->getSourcePath();
}
public function getSourceDirectory()
{
return $this->asset->getSourceDirectory();
}
public function getTargetPath()
{
return $this->asset->getTargetPath();
}
public function setTargetPath($targetPath)
{
$this->asset->setTargetPath($targetPath);
}
public function getLastModified()
{
return $this->asset->getLastModified();
}
public function getVars()
{
return $this->asset->getVars();
}
public function setValues(array $values)
{
$this->asset->setValues($values);
}
public function getValues()
{
return $this->asset->getValues();
}
/**
* Returns a cache key for the current asset.
*
* The key is composed of everything but an asset's content:
*
* * source root
* * source path
* * target url
* * last modified
* * filters
*
* @param AssetInterface $asset The asset
* @param FilterInterface $additionalFilter Any additional filter being applied
* @param string $salt Salt for the key
*
* @return string A key for identifying the current asset
*/
private static function getCacheKey(AssetInterface $asset, FilterInterface $additionalFilter = null, $salt = '')
{
if ($additionalFilter) {
$asset = clone $asset;
$asset->ensureFilter($additionalFilter);
}
$cacheKey = $asset->getSourceRoot();
$cacheKey .= $asset->getSourcePath();
$cacheKey .= $asset->getTargetPath();
$cacheKey .= $asset->getLastModified();
foreach ($asset->getFilters() as $filter) {
if ($filter instanceof HashableInterface) {
$cacheKey .= $filter->hash();
} else {
$cacheKey .= serialize($filter);
}
}
if ($values = $asset->getValues()) {
asort($values);
$cacheKey .= serialize($values);
}
return md5($cacheKey.$salt);
}
}
src/Assetic/Asset/GlobAsset.php 0000666 00000005330 13436750441 0012415 0 ustar 00
*/
class GlobAsset extends AssetCollection
{
private $globs;
private $initialized;
/**
* Constructor.
*
* @param string|array $globs A single glob path or array of paths
* @param array $filters An array of filters
* @param string $root The root directory
* @param array $vars
*/
public function __construct($globs, $filters = array(), $root = null, array $vars = array())
{
$this->globs = (array) $globs;
$this->initialized = false;
parent::__construct(array(), $filters, $root, $vars);
}
public function all()
{
if (!$this->initialized) {
$this->initialize();
}
return parent::all();
}
public function load(FilterInterface $additionalFilter = null)
{
if (!$this->initialized) {
$this->initialize();
}
parent::load($additionalFilter);
}
public function dump(FilterInterface $additionalFilter = null)
{
if (!$this->initialized) {
$this->initialize();
}
return parent::dump($additionalFilter);
}
public function getLastModified()
{
if (!$this->initialized) {
$this->initialize();
}
return parent::getLastModified();
}
public function getIterator()
{
if (!$this->initialized) {
$this->initialize();
}
return parent::getIterator();
}
public function setValues(array $values)
{
parent::setValues($values);
$this->initialized = false;
}
/**
* Initializes the collection based on the glob(s) passed in.
*/
private function initialize()
{
foreach ($this->globs as $glob) {
$glob = VarUtils::resolve($glob, $this->getVars(), $this->getValues());
if (false !== $paths = glob($glob)) {
foreach ($paths as $path) {
if (is_file($path)) {
$asset = new FileAsset($path, array(), $this->getSourceRoot(), null, $this->getVars());
$asset->setValues($this->getValues());
$this->add($asset);
}
}
}
}
$this->initialized = true;
}
}
src/Assetic/Asset/FileAsset.php 0000666 00000004505 13436750441 0012414 0 ustar 00
*/
class FileAsset extends BaseAsset
{
private $source;
/**
* Constructor.
*
* @param string $source An absolute path
* @param array $filters An array of filters
* @param string $sourceRoot The source asset root directory
* @param string $sourcePath The source asset path
* @param array $vars
*
* @throws \InvalidArgumentException If the supplied root doesn't match the source when guessing the path
*/
public function __construct($source, $filters = array(), $sourceRoot = null, $sourcePath = null, array $vars = array())
{
if (null === $sourceRoot) {
$sourceRoot = dirname($source);
if (null === $sourcePath) {
$sourcePath = basename($source);
}
} elseif (null === $sourcePath) {
if (0 !== strpos($source, $sourceRoot)) {
throw new \InvalidArgumentException(sprintf('The source "%s" is not in the root directory "%s"', $source, $sourceRoot));
}
$sourcePath = substr($source, strlen($sourceRoot) + 1);
}
$this->source = $source;
parent::__construct($filters, $sourceRoot, $sourcePath, $vars);
}
public function load(FilterInterface $additionalFilter = null)
{
$source = VarUtils::resolve($this->source, $this->getVars(), $this->getValues());
if (!is_file($source)) {
throw new \RuntimeException(sprintf('The source file "%s" does not exist.', $source));
}
$this->doLoad(file_get_contents($source), $additionalFilter);
}
public function getLastModified()
{
$source = VarUtils::resolve($this->source, $this->getVars(), $this->getValues());
if (!is_file($source)) {
throw new \RuntimeException(sprintf('The source file "%s" does not exist.', $source));
}
return filemtime($source);
}
}
src/Assetic/Asset/Iterator/AssetCollectionFilterIterator.php 0000666 00000004477 13436750441 0020311 0 ustar 00
*/
class AssetCollectionFilterIterator extends \RecursiveFilterIterator
{
private $visited;
private $sources;
/**
* Constructor.
*
* @param AssetCollectionIterator $iterator The inner iterator
* @param array $visited An array of visited asset objects
* @param array $sources An array of visited source strings
*/
public function __construct(AssetCollectionIterator $iterator, array $visited = array(), array $sources = array())
{
parent::__construct($iterator);
$this->visited = $visited;
$this->sources = $sources;
}
/**
* Determines whether the current asset is a duplicate.
*
* De-duplication is performed based on either strict equality or by
* matching sources.
*
* @return Boolean Returns true if we have not seen this asset yet
*/
public function accept()
{
$asset = $this->getInnerIterator()->current(true);
$duplicate = false;
// check strict equality
if (in_array($asset, $this->visited, true)) {
$duplicate = true;
} else {
$this->visited[] = $asset;
}
// check source
$sourceRoot = $asset->getSourceRoot();
$sourcePath = $asset->getSourcePath();
if ($sourceRoot && $sourcePath) {
$source = $sourceRoot.'/'.$sourcePath;
if (in_array($source, $this->sources)) {
$duplicate = true;
} else {
$this->sources[] = $source;
}
}
return !$duplicate;
}
/**
* Passes visited objects and source URLs to the child iterator.
*/
public function getChildren()
{
return new self($this->getInnerIterator()->getChildren(), $this->visited, $this->sources);
}
}
src/Assetic/Asset/Iterator/AssetCollectionIterator.php 0000666 00000006162 13436750441 0017134 0 ustar 00
*/
class AssetCollectionIterator implements \RecursiveIterator
{
private $assets;
private $filters;
private $vars;
private $output;
private $clones;
public function __construct(AssetCollectionInterface $coll, \SplObjectStorage $clones)
{
$this->assets = $coll->all();
$this->filters = $coll->getFilters();
$this->vars = $coll->getVars();
$this->output = $coll->getTargetPath();
$this->clones = $clones;
if (false === $pos = strrpos($this->output, '.')) {
$this->output .= '_*';
} else {
$this->output = substr($this->output, 0, $pos).'_*'.substr($this->output, $pos);
}
}
/**
* Returns a copy of the current asset with filters and a target URL applied.
*
* @param Boolean $raw Returns the unmodified asset if true
*
* @return \Assetic\Asset\AssetInterface
*/
public function current($raw = false)
{
$asset = current($this->assets);
if ($raw) {
return $asset;
}
// clone once
if (!isset($this->clones[$asset])) {
$clone = $this->clones[$asset] = clone $asset;
// generate a target path based on asset name
$name = sprintf('%s_%d', pathinfo($asset->getSourcePath(), PATHINFO_FILENAME) ?: 'part', $this->key() + 1);
$name = $this->removeDuplicateVar($name);
$clone->setTargetPath(str_replace('*', $name, $this->output));
} else {
$clone = $this->clones[$asset];
}
// cascade filters
foreach ($this->filters as $filter) {
$clone->ensureFilter($filter);
}
return $clone;
}
public function key()
{
return key($this->assets);
}
public function next()
{
return next($this->assets);
}
public function rewind()
{
return reset($this->assets);
}
public function valid()
{
return false !== current($this->assets);
}
public function hasChildren()
{
return current($this->assets) instanceof AssetCollectionInterface;
}
/**
* @uses current()
*/
public function getChildren()
{
return new self($this->current(), $this->clones);
}
private function removeDuplicateVar($name)
{
foreach ($this->vars as $var) {
$var = '{'.$var.'}';
if (false !== strpos($name, $var) && false !== strpos($this->output, $var)) {
$name = str_replace($var, '', $name);
}
}
return $name;
}
}
src/Assetic/Asset/BaseAsset.php 0000666 00000010406 13436750441 0012404 0 ustar 00
*/
abstract class BaseAsset implements AssetInterface
{
private $filters;
private $sourceRoot;
private $sourcePath;
private $sourceDir;
private $targetPath;
private $content;
private $loaded;
private $vars;
private $values;
/**
* Constructor.
*
* @param array $filters Filters for the asset
* @param string $sourceRoot The root directory
* @param string $sourcePath The asset path
* @param array $vars
*/
public function __construct($filters = array(), $sourceRoot = null, $sourcePath = null, array $vars = array())
{
$this->filters = new FilterCollection($filters);
$this->sourceRoot = $sourceRoot;
$this->sourcePath = $sourcePath;
if ($sourcePath && $sourceRoot) {
$this->sourceDir = dirname("$sourceRoot/$sourcePath");
}
$this->vars = $vars;
$this->values = array();
$this->loaded = false;
}
public function __clone()
{
$this->filters = clone $this->filters;
}
public function ensureFilter(FilterInterface $filter)
{
$this->filters->ensure($filter);
}
public function getFilters()
{
return $this->filters->all();
}
public function clearFilters()
{
$this->filters->clear();
}
/**
* Encapsulates asset loading logic.
*
* @param string $content The asset content
* @param FilterInterface $additionalFilter An additional filter
*/
protected function doLoad($content, FilterInterface $additionalFilter = null)
{
$filter = clone $this->filters;
if ($additionalFilter) {
$filter->ensure($additionalFilter);
}
$asset = clone $this;
$asset->setContent($content);
$filter->filterLoad($asset);
$this->content = $asset->getContent();
$this->loaded = true;
}
public function dump(FilterInterface $additionalFilter = null)
{
if (!$this->loaded) {
$this->load();
}
$filter = clone $this->filters;
if ($additionalFilter) {
$filter->ensure($additionalFilter);
}
$asset = clone $this;
$filter->filterDump($asset);
return $asset->getContent();
}
public function getContent()
{
return $this->content;
}
public function setContent($content)
{
$this->content = $content;
}
public function getSourceRoot()
{
return $this->sourceRoot;
}
public function getSourcePath()
{
return $this->sourcePath;
}
public function getSourceDirectory()
{
return $this->sourceDir;
}
public function getTargetPath()
{
return $this->targetPath;
}
public function setTargetPath($targetPath)
{
if ($this->vars) {
foreach ($this->vars as $var) {
if (false === strpos($targetPath, $var)) {
throw new \RuntimeException(sprintf('The asset target path "%s" must contain the variable "{%s}".', $targetPath, $var));
}
}
}
$this->targetPath = $targetPath;
}
public function getVars()
{
return $this->vars;
}
public function setValues(array $values)
{
foreach ($values as $var => $v) {
if (!in_array($var, $this->vars, true)) {
throw new \InvalidArgumentException(sprintf('The asset with source path "%s" has no variable named "%s".', $this->sourcePath, $var));
}
}
$this->values = $values;
$this->loaded = false;
}
public function getValues()
{
return $this->values;
}
}
src/Assetic/Asset/StringAsset.php 0000666 00000002447 13436750441 0013006 0 ustar 00
*/
class StringAsset extends BaseAsset
{
private $string;
private $lastModified;
/**
* Constructor.
*
* @param string $content The content of the asset
* @param array $filters Filters for the asset
* @param string $sourceRoot The source asset root directory
* @param string $sourcePath The source asset path
*/
public function __construct($content, $filters = array(), $sourceRoot = null, $sourcePath = null)
{
$this->string = $content;
parent::__construct($filters, $sourceRoot, $sourcePath);
}
public function load(FilterInterface $additionalFilter = null)
{
$this->doLoad($this->string, $additionalFilter);
}
public function setLastModified($lastModified)
{
$this->lastModified = $lastModified;
}
public function getLastModified()
{
return $this->lastModified;
}
}
src/Assetic/Asset/AssetReference.php 0000666 00000006511 13436750441 0013432 0 ustar 00
*/
class AssetReference implements AssetInterface
{
private $am;
private $name;
private $filters = array();
private $clone = false;
private $asset;
public function __construct(AssetManager $am, $name)
{
$this->am = $am;
$this->name = $name;
}
public function __clone()
{
$this->clone = true;
if ($this->asset) {
$this->asset = clone $this->asset;
}
}
public function ensureFilter(FilterInterface $filter)
{
$this->filters[] = $filter;
}
public function getFilters()
{
$this->flushFilters();
return $this->callAsset(__FUNCTION__);
}
public function clearFilters()
{
$this->filters = array();
$this->callAsset(__FUNCTION__);
}
public function load(FilterInterface $additionalFilter = null)
{
$this->flushFilters();
return $this->callAsset(__FUNCTION__, array($additionalFilter));
}
public function dump(FilterInterface $additionalFilter = null)
{
$this->flushFilters();
return $this->callAsset(__FUNCTION__, array($additionalFilter));
}
public function getContent()
{
return $this->callAsset(__FUNCTION__);
}
public function setContent($content)
{
$this->callAsset(__FUNCTION__, array($content));
}
public function getSourceRoot()
{
return $this->callAsset(__FUNCTION__);
}
public function getSourcePath()
{
return $this->callAsset(__FUNCTION__);
}
public function getSourceDirectory()
{
return $this->callAsset(__FUNCTION__);
}
public function getTargetPath()
{
return $this->callAsset(__FUNCTION__);
}
public function setTargetPath($targetPath)
{
$this->callAsset(__FUNCTION__, array($targetPath));
}
public function getLastModified()
{
return $this->callAsset(__FUNCTION__);
}
public function getVars()
{
return $this->callAsset(__FUNCTION__);
}
public function getValues()
{
return $this->callAsset(__FUNCTION__);
}
public function setValues(array $values)
{
$this->callAsset(__FUNCTION__, array($values));
}
// private
private function callAsset($method, $arguments = array())
{
$asset = $this->resolve();
return call_user_func_array(array($asset, $method), $arguments);
}
private function flushFilters()
{
$asset = $this->resolve();
while ($filter = array_shift($this->filters)) {
$asset->ensureFilter($filter);
}
}
private function resolve()
{
if ($this->asset) {
return $this->asset;
}
$asset = $this->am->get($this->name);
if ($this->clone) {
$asset = $this->asset = clone $asset;
}
return $asset;
}
}
src/Assetic/Factory/Resource/FileResource.php 0000666 00000001725 13436750441 0015244 0 ustar 00
*/
class FileResource implements ResourceInterface
{
private $path;
/**
* Constructor.
*
* @param string $path The path to a file
*/
public function __construct($path)
{
$this->path = $path;
}
public function isFresh($timestamp)
{
return file_exists($this->path) && filemtime($this->path) <= $timestamp;
}
public function getContent()
{
return file_exists($this->path) ? file_get_contents($this->path) : '';
}
public function __toString()
{
return $this->path;
}
}
src/Assetic/Factory/Resource/DirectoryResource.php 0000666 00000006171 13436750441 0016331 0 ustar 00
*/
class DirectoryResource implements IteratorResourceInterface
{
private $path;
private $pattern;
/**
* Constructor.
*
* @param string $path A directory path
* @param string $pattern A filename pattern
*/
public function __construct($path, $pattern = null)
{
if (DIRECTORY_SEPARATOR != substr($path, -1)) {
$path .= DIRECTORY_SEPARATOR;
}
$this->path = $path;
$this->pattern = $pattern;
}
public function isFresh($timestamp)
{
if (!is_dir($this->path) || filemtime($this->path) > $timestamp) {
return false;
}
foreach ($this as $resource) {
if (!$resource->isFresh($timestamp)) {
return false;
}
}
return true;
}
/**
* Returns the combined content of all inner resources.
*/
public function getContent()
{
$content = array();
foreach ($this as $resource) {
$content[] = $resource->getContent();
}
return implode("\n", $content);
}
public function __toString()
{
return $this->path;
}
public function getIterator()
{
return is_dir($this->path)
? new DirectoryResourceIterator($this->getInnerIterator())
: new \EmptyIterator();
}
protected function getInnerIterator()
{
return new DirectoryResourceFilterIterator(new \RecursiveDirectoryIterator($this->path, \RecursiveDirectoryIterator::FOLLOW_SYMLINKS), $this->pattern);
}
}
/**
* An iterator that converts file objects into file resources.
*
* @author Kris Wallsmith
* @access private
*/
class DirectoryResourceIterator extends \RecursiveIteratorIterator
{
public function current()
{
return new FileResource(parent::current()->getPathname());
}
}
/**
* Filters files by a basename pattern.
*
* @author Kris Wallsmith
* @access private
*/
class DirectoryResourceFilterIterator extends \RecursiveFilterIterator
{
protected $pattern;
public function __construct(\RecursiveDirectoryIterator $iterator, $pattern = null)
{
parent::__construct($iterator);
$this->pattern = $pattern;
}
public function accept()
{
$file = $this->current();
$name = $file->getBasename();
if ($file->isDir()) {
return '.' != $name[0];
}
return null === $this->pattern || 0 < preg_match($this->pattern, $name);
}
public function getChildren()
{
return new self(new \RecursiveDirectoryIterator($this->current()->getPathname(), \RecursiveDirectoryIterator::FOLLOW_SYMLINKS), $this->pattern);
}
}
src/Assetic/Factory/Resource/ResourceInterface.php 0000666 00000001752 13436750441 0016265 0 ustar 00
*/
interface ResourceInterface
{
/**
* Checks if a timestamp represents the latest resource.
*
* @param integer $timestamp A UNIX timestamp
*
* @return Boolean True if the timestamp is up to date
*/
public function isFresh($timestamp);
/**
* Returns the content of the resource.
*
* @return string The content
*/
public function getContent();
/**
* Returns a unique string for the current resource.
*
* @return string A unique string to identity the current resource
*/
public function __toString();
}
src/Assetic/Factory/Resource/CoalescingDirectoryResource.php 0000666 00000005246 13436750441 0020323 0 ustar 00
*/
class CoalescingDirectoryResource implements IteratorResourceInterface
{
private $directories;
public function __construct($directories)
{
$this->directories = array();
foreach ($directories as $directory) {
$this->addDirectory($directory);
}
}
public function addDirectory(IteratorResourceInterface $directory)
{
$this->directories[] = $directory;
}
public function isFresh($timestamp)
{
foreach ($this->getFileResources() as $file) {
if (!$file->isFresh($timestamp)) {
return false;
}
}
return true;
}
public function getContent()
{
$parts = array();
foreach ($this->getFileResources() as $file) {
$parts[] = $file->getContent();
}
return implode("\n", $parts);
}
/**
* Returns a string to uniquely identify the current resource.
*
* @return string An identifying string
*/
public function __toString()
{
$parts = array();
foreach ($this->directories as $directory) {
$parts[] = (string) $directory;
}
return implode(',', $parts);
}
public function getIterator()
{
return new \ArrayIterator($this->getFileResources());
}
/**
* Returns the relative version of a filename.
*
* @param ResourceInterface $file The file
* @param ResourceInterface $directory The directory
*
* @return string The name to compare with files from other directories
*/
protected function getRelativeName(ResourceInterface $file, ResourceInterface $directory)
{
return substr((string) $file, strlen((string) $directory));
}
/**
* Performs the coalesce.
*
* @return array An array of file resources
*/
private function getFileResources()
{
$paths = array();
foreach ($this->directories as $directory) {
foreach ($directory as $file) {
$relative = $this->getRelativeName($file, $directory);
if (!isset($paths[$relative])) {
$paths[$relative] = $file;
}
}
}
return array_values($paths);
}
}
src/Assetic/Factory/Resource/IteratorResourceInterface.php 0000666 00000000755 13436750441 0020001 0 ustar 00
*/
interface IteratorResourceInterface extends ResourceInterface, \IteratorAggregate
{
}
src/Assetic/Factory/LazyAssetManager.php 0000666 00000012237 13436750441 0014300 0 ustar 00
*/
class LazyAssetManager extends AssetManager
{
private $factory;
private $loaders;
private $resources;
private $formulae;
private $loaded;
private $loading;
/**
* Constructor.
*
* @param AssetFactory $factory The asset factory
* @param array $loaders An array of loaders indexed by alias
*/
public function __construct(AssetFactory $factory, $loaders = array())
{
$this->factory = $factory;
$this->loaders = array();
$this->resources = array();
$this->formulae = array();
$this->loaded = false;
$this->loading = false;
foreach ($loaders as $alias => $loader) {
$this->setLoader($alias, $loader);
}
}
/**
* Adds a loader to the asset manager.
*
* @param string $alias An alias for the loader
* @param FormulaLoaderInterface $loader A loader
*/
public function setLoader($alias, FormulaLoaderInterface $loader)
{
$this->loaders[$alias] = $loader;
$this->loaded = false;
}
/**
* Adds a resource to the asset manager.
*
* @param ResourceInterface $resource A resource
* @param string $loader The loader alias for this resource
*/
public function addResource(ResourceInterface $resource, $loader)
{
$this->resources[$loader][] = $resource;
$this->loaded = false;
}
/**
* Returns an array of resources.
*
* @return array An array of resources
*/
public function getResources()
{
$resources = array();
foreach ($this->resources as $r) {
$resources = array_merge($resources, $r);
}
return $resources;
}
/**
* Checks for an asset formula.
*
* @param string $name An asset name
*
* @return Boolean If there is a formula
*/
public function hasFormula($name)
{
if (!$this->loaded) {
$this->load();
}
return isset($this->formulae[$name]);
}
/**
* Returns an asset's formula.
*
* @param string $name An asset name
*
* @return array The formula
*
* @throws \InvalidArgumentException If there is no formula by that name
*/
public function getFormula($name)
{
if (!$this->loaded) {
$this->load();
}
if (!isset($this->formulae[$name])) {
throw new \InvalidArgumentException(sprintf('There is no "%s" formula.', $name));
}
return $this->formulae[$name];
}
/**
* Sets a formula on the asset manager.
*
* @param string $name An asset name
* @param array $formula A formula
*/
public function setFormula($name, array $formula)
{
$this->formulae[$name] = $formula;
}
/**
* Loads formulae from resources.
*
* @throws \LogicException If a resource has been added to an invalid loader
*/
public function load()
{
if ($this->loading) {
return;
}
if ($diff = array_diff(array_keys($this->resources), array_keys($this->loaders))) {
throw new \LogicException('The following loader(s) are not registered: '.implode(', ', $diff));
}
$this->loading = true;
foreach ($this->resources as $loader => $resources) {
foreach ($resources as $resource) {
$this->formulae = array_replace($this->formulae, $this->loaders[$loader]->load($resource));
}
}
$this->loaded = true;
$this->loading = false;
}
public function get($name)
{
if (!$this->loaded) {
$this->load();
}
if (!parent::has($name) && isset($this->formulae[$name])) {
list($inputs, $filters, $options) = $this->formulae[$name];
$options['name'] = $name;
parent::set($name, $this->factory->createAsset($inputs, $filters, $options));
}
return parent::get($name);
}
public function has($name)
{
if (!$this->loaded) {
$this->load();
}
return isset($this->formulae[$name]) || parent::has($name);
}
public function getNames()
{
if (!$this->loaded) {
$this->load();
}
return array_unique(array_merge(parent::getNames(), array_keys($this->formulae)));
}
public function isDebug()
{
return $this->factory->isDebug();
}
public function getLastModified(AssetInterface $asset)
{
return $this->factory->getLastModified($asset);
}
}
src/Assetic/Factory/Worker/EnsureFilterWorker.php 0000666 00000003333 13436750441 0016135 0 ustar 00
* @todo A better asset-matcher mechanism
*/
class EnsureFilterWorker implements WorkerInterface
{
const CHECK_SOURCE = 1;
const CHECK_TARGET = 2;
private $pattern;
private $filter;
private $flags;
/**
* Constructor.
*
* @param string $pattern A regex for checking the asset's target URL
* @param FilterInterface $filter A filter to apply if the regex matches
* @param integer $flags Flags for what to check
*/
public function __construct($pattern, FilterInterface $filter, $flags = null)
{
if (null === $flags) {
$flags = self::CHECK_SOURCE | self::CHECK_TARGET;
}
$this->pattern = $pattern;
$this->filter = $filter;
$this->flags = $flags;
}
public function process(AssetInterface $asset, AssetFactory $factory)
{
if (
(self::CHECK_SOURCE === (self::CHECK_SOURCE & $this->flags) && preg_match($this->pattern, $asset->getSourcePath()))
||
(self::CHECK_TARGET === (self::CHECK_TARGET & $this->flags) && preg_match($this->pattern, $asset->getTargetPath()))
) {
$asset->ensureFilter($this->filter);
}
}
}
src/Assetic/Factory/Worker/WorkerInterface.php 0000666 00000001473 13436750441 0015431 0 ustar 00
*/
interface WorkerInterface
{
/**
* Processes an asset.
*
* @param AssetInterface $asset An asset
* @param AssetFactory $factory The factory
*
* @return AssetInterface|null May optionally return a replacement asset
*/
public function process(AssetInterface $asset, AssetFactory $factory);
}
src/Assetic/Factory/Worker/CacheBustingWorker.php 0000666 00000003423 13436750441 0016065 0 ustar 00
*/
class CacheBustingWorker implements WorkerInterface
{
private $separator;
public function __construct($separator = '-')
{
$this->separator = $separator;
}
public function process(AssetInterface $asset, AssetFactory $factory)
{
if (!$path = $asset->getTargetPath()) {
// no path to work with
return;
}
if (!$search = pathinfo($path, PATHINFO_EXTENSION)) {
// nothing to replace
return;
}
$replace = $this->separator.$this->getHash($asset, $factory).'.'.$search;
if (preg_match('/'.preg_quote($replace, '/').'$/', $path)) {
// already replaced
return;
}
$asset->setTargetPath(
preg_replace('/\.'.preg_quote($search, '/').'$/', $replace, $path)
);
}
protected function getHash(AssetInterface $asset, AssetFactory $factory)
{
$hash = hash_init('sha1');
hash_update($hash, $factory->getLastModified($asset));
if ($asset instanceof AssetCollectionInterface) {
foreach ($asset as $i => $leaf) {
$sourcePath = $leaf->getSourcePath();
hash_update($hash, $sourcePath ?: $i);
}
}
return substr(hash_final($hash), 0, 7);
}
}
src/Assetic/Factory/Loader/BasePhpFormulaLoader.php 0000666 00000011631 13436750441 0016270 0 ustar 00
*/
abstract class BasePhpFormulaLoader implements FormulaLoaderInterface
{
protected $factory;
protected $prototypes;
public function __construct(AssetFactory $factory)
{
$this->factory = $factory;
$this->prototypes = array();
foreach ($this->registerPrototypes() as $prototype => $options) {
$this->addPrototype($prototype, $options);
}
}
public function addPrototype($prototype, array $options = array())
{
$tokens = token_get_all('prototypes[$prototype] = array($tokens, $options);
}
public function load(ResourceInterface $resource)
{
if (!$nbProtos = count($this->prototypes)) {
throw new \LogicException('There are no prototypes registered.');
}
$buffers = array_fill(0, $nbProtos, '');
$bufferLevels = array_fill(0, $nbProtos, 0);
$buffersInWildcard = array();
$tokens = token_get_all($resource->getContent());
$calls = array();
while ($token = array_shift($tokens)) {
$current = self::tokenToString($token);
// loop through each prototype (by reference)
foreach (array_keys($this->prototypes) as $i) {
$prototype = & $this->prototypes[$i][0];
$options = $this->prototypes[$i][1];
$buffer = & $buffers[$i];
$level = & $bufferLevels[$i];
if (isset($buffersInWildcard[$i])) {
switch ($current) {
case '(': ++$level; break;
case ')': --$level; break;
}
$buffer .= $current;
if (!$level) {
$calls[] = array($buffer.';', $options);
$buffer = '';
unset($buffersInWildcard[$i]);
}
} elseif ($current == self::tokenToString(current($prototype))) {
$buffer .= $current;
if ('*' == self::tokenToString(next($prototype))) {
$buffersInWildcard[$i] = true;
++$level;
}
} else {
reset($prototype);
unset($buffersInWildcard[$i]);
$buffer = '';
}
}
}
$formulae = array();
foreach ($calls as $call) {
$formulae += call_user_func_array(array($this, 'processCall'), $call);
}
return $formulae;
}
private function processCall($call, array $protoOptions = array())
{
$tmp = FilesystemUtils::createTemporaryFile('php_formula_loader');
file_put_contents($tmp, implode("\n", array(
'registerSetupCode(),
$call,
'echo serialize($_call);',
)));
$args = unserialize(shell_exec('php '.escapeshellarg($tmp)));
unlink($tmp);
$inputs = isset($args[0]) ? self::argumentToArray($args[0]) : array();
$filters = isset($args[1]) ? self::argumentToArray($args[1]) : array();
$options = isset($args[2]) ? $args[2] : array();
if (!isset($options['debug'])) {
$options['debug'] = $this->factory->isDebug();
}
if (!is_array($options)) {
throw new \RuntimeException('The third argument must be omitted, null or an array.');
}
// apply the prototype options
$options += $protoOptions;
if (!isset($options['name'])) {
$options['name'] = $this->factory->generateAssetName($inputs, $filters, $options);
}
return array($options['name'] => array($inputs, $filters, $options));
}
/**
* Returns an array of prototypical calls and options.
*
* @return array Prototypes and options
*/
abstract protected function registerPrototypes();
/**
* Returns setup code for the reflection scriptlet.
*
* @return string Some PHP setup code
*/
abstract protected function registerSetupCode();
protected static function tokenToString($token)
{
return is_array($token) ? $token[1] : $token;
}
protected static function argumentToArray($argument)
{
return is_array($argument) ? $argument : array_filter(array_map('trim', explode(',', $argument)));
}
}
src/Assetic/Factory/Loader/CachedFormulaLoader.php 0000666 00000003716 13436750441 0016122 0 ustar 00
*/
class CachedFormulaLoader implements FormulaLoaderInterface
{
private $loader;
private $configCache;
private $debug;
/**
* Constructor.
*
* When the loader is in debug mode it will ensure the cached formulae
* are fresh before returning them.
*
* @param FormulaLoaderInterface $loader A formula loader
* @param ConfigCache $configCache A config cache
* @param Boolean $debug The debug mode
*/
public function __construct(FormulaLoaderInterface $loader, ConfigCache $configCache, $debug = false)
{
$this->loader = $loader;
$this->configCache = $configCache;
$this->debug = $debug;
}
public function load(ResourceInterface $resources)
{
if (!$resources instanceof IteratorResourceInterface) {
$resources = array($resources);
}
$formulae = array();
foreach ($resources as $resource) {
$id = (string) $resource;
if (!$this->configCache->has($id) || ($this->debug && !$resource->isFresh($this->configCache->getTimestamp($id)))) {
$formulae += $this->loader->load($resource);
$this->configCache->set($id, $formulae);
} else {
$formulae += $this->configCache->get($id);
}
}
return $formulae;
}
}
src/Assetic/Factory/Loader/FormulaLoaderInterface.php 0000666 00000001456 13436750441 0016652 0 ustar 00
*/
interface FormulaLoaderInterface
{
/**
* Loads formulae from a resource.
*
* Formulae should be loaded the same regardless of the current debug
* mode. Debug considerations should happen downstream.
*
* @param ResourceInterface $resource A resource
*
* @return array An array of formulae
*/
public function load(ResourceInterface $resource);
}
src/Assetic/Factory/Loader/FunctionCallsFormulaLoader.php 0000666 00000002103 13436750441 0017504 0 ustar 00
*/
class FunctionCallsFormulaLoader extends BasePhpFormulaLoader
{
protected function registerPrototypes()
{
return array(
'assetic_javascripts(*)' => array('output' => 'js/*.js'),
'assetic_stylesheets(*)' => array('output' => 'css/*.css'),
'assetic_image(*)' => array('output' => 'images/*'),
);
}
protected function registerSetupCode()
{
return <<<'EOF'
function assetic_javascripts()
{
global $_call;
$_call = func_get_args();
}
function assetic_stylesheets()
{
global $_call;
$_call = func_get_args();
}
function assetic_image()
{
global $_call;
$_call = func_get_args();
}
EOF;
}
}
src/Assetic/Factory/AssetFactory.php 0000666 00000030241 13436750441 0013470 0 ustar 00
*/
class AssetFactory
{
private $root;
private $debug;
private $output;
private $workers;
private $am;
private $fm;
/**
* Constructor.
*
* @param string $root The default root directory
* @param Boolean $debug Filters prefixed with a "?" will be omitted in debug mode
*/
public function __construct($root, $debug = false)
{
$this->root = rtrim($root, '/');
$this->debug = $debug;
$this->output = 'assetic/*';
$this->workers = array();
}
/**
* Sets debug mode for the current factory.
*
* @param Boolean $debug Debug mode
*/
public function setDebug($debug)
{
$this->debug = $debug;
}
/**
* Checks if the factory is in debug mode.
*
* @return Boolean Debug mode
*/
public function isDebug()
{
return $this->debug;
}
/**
* Sets the default output string.
*
* @param string $output The default output string
*/
public function setDefaultOutput($output)
{
$this->output = $output;
}
/**
* Adds a factory worker.
*
* @param WorkerInterface $worker A worker
*/
public function addWorker(WorkerInterface $worker)
{
$this->workers[] = $worker;
}
/**
* Returns the current asset manager.
*
* @return AssetManager|null The asset manager
*/
public function getAssetManager()
{
return $this->am;
}
/**
* Sets the asset manager to use when creating asset references.
*
* @param AssetManager $am The asset manager
*/
public function setAssetManager(AssetManager $am)
{
$this->am = $am;
}
/**
* Returns the current filter manager.
*
* @return FilterManager|null The filter manager
*/
public function getFilterManager()
{
return $this->fm;
}
/**
* Sets the filter manager to use when adding filters.
*
* @param FilterManager $fm The filter manager
*/
public function setFilterManager(FilterManager $fm)
{
$this->fm = $fm;
}
/**
* Creates a new asset.
*
* Prefixing a filter name with a question mark will cause it to be
* omitted when the factory is in debug mode.
*
* Available options:
*
* * output: An output string
* * name: An asset name for interpolation in output patterns
* * debug: Forces debug mode on or off for this asset
* * root: An array or string of more root directories
*
* @param array|string $inputs An array of input strings
* @param array|string $filters An array of filter names
* @param array $options An array of options
*
* @return AssetCollection An asset collection
*/
public function createAsset($inputs = array(), $filters = array(), array $options = array())
{
if (!is_array($inputs)) {
$inputs = array($inputs);
}
if (!is_array($filters)) {
$filters = array($filters);
}
if (!isset($options['output'])) {
$options['output'] = $this->output;
}
if (!isset($options['vars'])) {
$options['vars'] = array();
}
if (!isset($options['debug'])) {
$options['debug'] = $this->debug;
}
if (!isset($options['root'])) {
$options['root'] = array($this->root);
} else {
if (!is_array($options['root'])) {
$options['root'] = array($options['root']);
}
$options['root'][] = $this->root;
}
if (!isset($options['name'])) {
$options['name'] = $this->generateAssetName($inputs, $filters, $options);
}
$asset = $this->createAssetCollection(array(), $options);
$extensions = array();
// inner assets
foreach ($inputs as $input) {
if (is_array($input)) {
// nested formula
$asset->add(call_user_func_array(array($this, 'createAsset'), $input));
} else {
$asset->add($this->parseInput($input, $options));
$extensions[pathinfo($input, PATHINFO_EXTENSION)] = true;
}
}
// filters
foreach ($filters as $filter) {
if ('?' != $filter[0]) {
$asset->ensureFilter($this->getFilter($filter));
} elseif (!$options['debug']) {
$asset->ensureFilter($this->getFilter(substr($filter, 1)));
}
}
// append variables
if (!empty($options['vars'])) {
$toAdd = array();
foreach ($options['vars'] as $var) {
if (false !== strpos($options['output'], '{'.$var.'}')) {
continue;
}
$toAdd[] = '{'.$var.'}';
}
if ($toAdd) {
$options['output'] = str_replace('*', '*.'.implode('.', $toAdd), $options['output']);
}
}
// append consensus extension if missing
if (1 == count($extensions) && !pathinfo($options['output'], PATHINFO_EXTENSION) && $extension = key($extensions)) {
$options['output'] .= '.'.$extension;
}
// output --> target url
$asset->setTargetPath(str_replace('*', $options['name'], $options['output']));
// apply workers and return
return $this->applyWorkers($asset);
}
public function generateAssetName($inputs, $filters, $options = array())
{
foreach (array_diff(array_keys($options), array('output', 'debug', 'root')) as $key) {
unset($options[$key]);
}
ksort($options);
return substr(sha1(serialize($inputs).serialize($filters).serialize($options)), 0, 7);
}
public function getLastModified(AssetInterface $asset)
{
$mtime = 0;
foreach ($asset instanceof AssetCollectionInterface ? $asset : array($asset) as $leaf) {
$mtime = max($mtime, $leaf->getLastModified());
if (!$filters = $leaf->getFilters()) {
continue;
}
$prevFilters = array();
foreach ($filters as $filter) {
$prevFilters[] = $filter;
if (!$filter instanceof DependencyExtractorInterface) {
continue;
}
// extract children from leaf after running all preceeding filters
$clone = clone $leaf;
$clone->clearFilters();
foreach (array_slice($prevFilters, 0, -1) as $prevFilter) {
$clone->ensureFilter($prevFilter);
}
$clone->load();
foreach ($filter->getChildren($this, $clone->getContent(), $clone->getSourceDirectory()) as $child) {
$mtime = max($mtime, $this->getLastModified($child));
}
}
}
return $mtime;
}
/**
* Parses an input string string into an asset.
*
* The input string can be one of the following:
*
* * A reference: If the string starts with an "at" sign it will be interpreted as a reference to an asset in the asset manager
* * An absolute URL: If the string contains "://" or starts with "//" it will be interpreted as an HTTP asset
* * A glob: If the string contains a "*" it will be interpreted as a glob
* * A path: Otherwise the string is interpreted as a filesystem path
*
* Both globs and paths will be absolutized using the current root directory.
*
* @param string $input An input string
* @param array $options An array of options
*
* @return AssetInterface An asset
*/
protected function parseInput($input, array $options = array())
{
if ('@' == $input[0]) {
return $this->createAssetReference(substr($input, 1));
}
if (false !== strpos($input, '://') || 0 === strpos($input, '//')) {
return $this->createHttpAsset($input, $options['vars']);
}
if (self::isAbsolutePath($input)) {
if ($root = self::findRootDir($input, $options['root'])) {
$path = ltrim(substr($input, strlen($root)), '/');
} else {
$path = null;
}
} else {
$root = $this->root;
$path = $input;
$input = $this->root.'/'.$path;
}
if (false !== strpos($input, '*')) {
return $this->createGlobAsset($input, $root, $options['vars']);
}
return $this->createFileAsset($input, $root, $path, $options['vars']);
}
protected function createAssetCollection(array $assets = array(), array $options = array())
{
return new AssetCollection($assets, array(), null, isset($options['vars']) ? $options['vars'] : array());
}
protected function createAssetReference($name)
{
if (!$this->am) {
throw new \LogicException('There is no asset manager.');
}
return new AssetReference($this->am, $name);
}
protected function createHttpAsset($sourceUrl, $vars)
{
return new HttpAsset($sourceUrl, array(), false, $vars);
}
protected function createGlobAsset($glob, $root = null, $vars)
{
return new GlobAsset($glob, array(), $root, $vars);
}
protected function createFileAsset($source, $root = null, $path = null, $vars)
{
return new FileAsset($source, array(), $root, $path, $vars);
}
protected function getFilter($name)
{
if (!$this->fm) {
throw new \LogicException('There is no filter manager.');
}
return $this->fm->get($name);
}
/**
* Filters an asset collection through the factory workers.
*
* Each leaf asset will be processed first, followed by the asset
* collection itself.
*
* @param AssetCollectionInterface $asset An asset collection
*
* @return AssetCollectionInterface
*/
private function applyWorkers(AssetCollectionInterface $asset)
{
foreach ($asset as $leaf) {
foreach ($this->workers as $worker) {
$retval = $worker->process($leaf, $this);
if ($retval instanceof AssetInterface && $leaf !== $retval) {
$asset->replaceLeaf($leaf, $retval);
}
}
}
foreach ($this->workers as $worker) {
$retval = $worker->process($asset, $this);
if ($retval instanceof AssetInterface) {
$asset = $retval;
}
}
return $asset instanceof AssetCollectionInterface ? $asset : $this->createAssetCollection(array($asset));
}
private static function isAbsolutePath($path)
{
return '/' == $path[0] || '\\' == $path[0] || (3 < strlen($path) && ctype_alpha($path[0]) && $path[1] == ':' && ('\\' == $path[2] || '/' == $path[2]));
}
/**
* Loops through the root directories and returns the first match.
*
* @param string $path An absolute path
* @param array $roots An array of root directories
*
* @return string|null The matching root directory, if found
*/
private static function findRootDir($path, array $roots)
{
foreach ($roots as $root) {
if (0 === strpos($path, $root)) {
return $root;
}
}
}
}
src/Assetic/AssetManager.php 0000666 00000003740 13436750441 0012030 0 ustar 00
*/
class AssetManager
{
private $assets = array();
/**
* Gets an asset by name.
*
* @param string $name The asset name
*
* @return AssetInterface The asset
*
* @throws \InvalidArgumentException If there is no asset by that name
*/
public function get($name)
{
if (!isset($this->assets[$name])) {
throw new \InvalidArgumentException(sprintf('There is no "%s" asset.', $name));
}
return $this->assets[$name];
}
/**
* Checks if the current asset manager has a certain asset.
*
* @param string $name an asset name
*
* @return Boolean True if the asset has been set, false if not
*/
public function has($name)
{
return isset($this->assets[$name]);
}
/**
* Registers an asset to the current asset manager.
*
* @param string $name The asset name
* @param AssetInterface $asset The asset
*
* @throws \InvalidArgumentException If the asset name is invalid
*/
public function set($name, AssetInterface $asset)
{
if (!ctype_alnum(str_replace('_', '', $name))) {
throw new \InvalidArgumentException(sprintf('The name "%s" is invalid.', $name));
}
$this->assets[$name] = $asset;
}
/**
* Returns an array of asset names.
*
* @return array An array of asset names
*/
public function getNames()
{
return array_keys($this->assets);
}
/**
* Clears all assets.
*/
public function clear()
{
$this->assets = array();
}
}
src/Assetic/Exception/Exception.php 0000666 00000000574 13436750441 0013354 0 ustar 00
*/
interface Exception
{
}
src/Assetic/Exception/FilterException.php 0000666 00000003275 13436750441 0014523 0 ustar 00
*/
class FilterException extends \RuntimeException implements Exception
{
private $originalMessage;
private $input;
public static function fromProcess(Process $proc)
{
$message = sprintf("An error occurred while running:\n%s", $proc->getCommandLine());
$errorOutput = $proc->getErrorOutput();
if (!empty($errorOutput)) {
$message .= "\n\nError Output:\n".str_replace("\r", '', $errorOutput);
}
$output = $proc->getOutput();
if (!empty($output)) {
$message .= "\n\nOutput:\n".str_replace("\r", '', $output);
}
return new self($message);
}
public function __construct($message, $code = 0, \Exception $previous = null)
{
parent::__construct($message, $code, $previous);
$this->originalMessage = $message;
}
public function setInput($input)
{
$this->input = $input;
$this->updateMessage();
return $this;
}
public function getInput()
{
return $this->input;
}
private function updateMessage()
{
$message = $this->originalMessage;
if (!empty($this->input)) {
$message .= "\n\nInput:\n".$this->input;
}
$this->message = $message;
}
}
src/Assetic/ValueSupplierInterface.php 0000666 00000001111 13436750441 0014065 0 ustar 00
*/
interface ValueSupplierInterface
{
/**
* Returns a map of values.
*
* @return array
*/
public function getValues();
}
src/Assetic/Extension/Twig/AsseticFilterFunction.php 0000666 00000001215 13436750441 0016606 0 ustar 00 false,
'needs_context' => false,
'node_class' => '\Assetic\Extension\Twig\AsseticFilterNode',
)));
}
}
src/Assetic/Extension/Twig/ValueContainer.php 0000666 00000003421 13436750441 0015257 0 ustar 00
*/
class ValueContainer implements \ArrayAccess, \IteratorAggregate, \Countable
{
private $values;
private $valueSupplier;
public function __construct(ValueSupplierInterface $valueSupplier)
{
$this->valueSupplier = $valueSupplier;
}
public function offsetExists($offset)
{
$this->initialize();
return array_key_exists($offset, $this->values);
}
public function offsetGet($offset)
{
$this->initialize();
if (!array_key_exists($offset, $this->values)) {
throw new \OutOfRangeException(sprintf('The variable "%s" does not exist.', $offset));
}
return $this->values[$offset];
}
public function offsetSet($offset, $value)
{
throw new \BadMethodCallException('The ValueContainer is read-only.');
}
public function offsetUnset($offset)
{
throw new \BadMethodCallException('The ValueContainer is read-only.');
}
public function getIterator()
{
$this->initialize();
return new \ArrayIterator($this->values);
}
public function count()
{
$this->initialize();
return count($this->values);
}
private function initialize()
{
if (null === $this->values) {
$this->values = $this->valueSupplier->getValues();
}
}
}
src/Assetic/Extension/Twig/AsseticNode.php 0000666 00000011717 13436750441 0014550 0 ustar 00 $body);
$attributes = array_replace(
array('debug' => null, 'combine' => null, 'var_name' => 'asset_url'),
$attributes,
array('asset' => $asset, 'inputs' => $inputs, 'filters' => $filters, 'name' => $name)
);
parent::__construct($nodes, $attributes, $lineno, $tag);
}
public function compile(\Twig_Compiler $compiler)
{
$compiler->addDebugInfo($this);
$combine = $this->getAttribute('combine');
$debug = $this->getAttribute('debug');
if (null === $combine && null !== $debug) {
$combine = !$debug;
}
if (null === $combine) {
$compiler
->write("if (isset(\$context['assetic']['debug']) && \$context['assetic']['debug']) {\n")
->indent()
;
$this->compileDebug($compiler);
$compiler
->outdent()
->write("} else {\n")
->indent()
;
$this->compileAsset($compiler, $this->getAttribute('asset'), $this->getAttribute('name'));
$compiler
->outdent()
->write("}\n")
;
} elseif ($combine) {
$this->compileAsset($compiler, $this->getAttribute('asset'), $this->getAttribute('name'));
} else {
$this->compileDebug($compiler);
}
$compiler
->write('unset($context[')
->repr($this->getAttribute('var_name'))
->raw("]);\n")
;
}
protected function compileDebug(\Twig_Compiler $compiler)
{
$i = 0;
foreach ($this->getAttribute('asset') as $leaf) {
$leafName = $this->getAttribute('name').'_'.$i++;
$this->compileAsset($compiler, $leaf, $leafName);
}
}
protected function compileAsset(\Twig_Compiler $compiler, AssetInterface $asset, $name)
{
if ($vars = $asset->getVars()) {
$compiler->write("// check variable conditions\n");
foreach ($vars as $var) {
$compiler
->write("if (!isset(\$context['assetic']['vars']['$var'])) {\n")
->indent()
->write("throw new \RuntimeException(sprintf('The asset \"".$name."\" expected variable \"".$var."\" to be set, but got only these vars: %s. Did you set-up a value supplier?', isset(\$context['assetic']['vars']) && \$context['assetic']['vars'] ? implode(', ', \$context['assetic']['vars']) : '# none #'));\n")
->outdent()
->write("}\n")
;
}
$compiler->raw("\n");
}
$compiler
->write("// asset \"$name\"\n")
->write('$context[')
->repr($this->getAttribute('var_name'))
->raw('] = ')
;
$this->compileAssetUrl($compiler, $asset, $name);
$compiler
->raw(";\n")
->subcompile($this->getNode('body'))
;
}
protected function compileAssetUrl(\Twig_Compiler $compiler, AssetInterface $asset, $name)
{
if (!$vars = $asset->getVars()) {
$compiler->repr($asset->getTargetPath());
return;
}
$compiler
->raw("strtr(")
->string($asset->getTargetPath())
->raw(", array(");
$first = true;
foreach ($vars as $var) {
if (!$first) {
$compiler->raw(", ");
}
$first = false;
$compiler
->string("{".$var."}")
->raw(" => \$context['assetic']['vars']['$var']")
;
}
$compiler
->raw("))")
;
}
}
src/Assetic/Extension/Twig/AsseticTokenParser.php 0000666 00000020035 13436750441 0016111 0 ustar 00 factory = $factory;
$this->tag = $tag;
$this->output = $output;
$this->single = $single;
$this->extensions = $extensions;
}
public function parse(\Twig_Token $token)
{
$inputs = array();
$filters = array();
$name = null;
$attributes = array(
'output' => $this->output,
'var_name' => 'asset_url',
'vars' => array(),
);
$stream = $this->parser->getStream();
while (!$stream->test(\Twig_Token::BLOCK_END_TYPE)) {
if ($stream->test(\Twig_Token::STRING_TYPE)) {
// '@jquery', 'js/src/core/*', 'js/src/extra.js'
$inputs[] = $stream->next()->getValue();
} elseif ($stream->test(\Twig_Token::NAME_TYPE, 'filter')) {
// filter='yui_js'
$stream->next();
$stream->expect(\Twig_Token::OPERATOR_TYPE, '=');
$filters = array_merge($filters, array_filter(array_map('trim', explode(',', $stream->expect(\Twig_Token::STRING_TYPE)->getValue()))));
} elseif ($stream->test(\Twig_Token::NAME_TYPE, 'output')) {
// output='js/packed/*.js' OR output='js/core.js'
$stream->next();
$stream->expect(\Twig_Token::OPERATOR_TYPE, '=');
$attributes['output'] = $stream->expect(\Twig_Token::STRING_TYPE)->getValue();
} elseif ($stream->test(\Twig_Token::NAME_TYPE, 'name')) {
// name='core_js'
$stream->next();
$stream->expect(\Twig_Token::OPERATOR_TYPE, '=');
$name = $stream->expect(\Twig_Token::STRING_TYPE)->getValue();
} elseif ($stream->test(\Twig_Token::NAME_TYPE, 'as')) {
// as='the_url'
$stream->next();
$stream->expect(\Twig_Token::OPERATOR_TYPE, '=');
$attributes['var_name'] = $stream->expect(\Twig_Token::STRING_TYPE)->getValue();
} elseif ($stream->test(\Twig_Token::NAME_TYPE, 'debug')) {
// debug=true
$stream->next();
$stream->expect(\Twig_Token::OPERATOR_TYPE, '=');
$attributes['debug'] = 'true' == $stream->expect(\Twig_Token::NAME_TYPE, array('true', 'false'))->getValue();
} elseif ($stream->test(\Twig_Token::NAME_TYPE, 'combine')) {
// combine=true
$stream->next();
$stream->expect(\Twig_Token::OPERATOR_TYPE, '=');
$attributes['combine'] = 'true' == $stream->expect(\Twig_Token::NAME_TYPE, array('true', 'false'))->getValue();
} elseif ($stream->test(\Twig_Token::NAME_TYPE, 'vars')) {
// vars=['locale','browser']
$stream->next();
$stream->expect(\Twig_Token::OPERATOR_TYPE, '=');
$stream->expect(\Twig_Token::PUNCTUATION_TYPE, '[');
while ($stream->test(\Twig_Token::STRING_TYPE)) {
$attributes['vars'][] = $stream->expect(\Twig_Token::STRING_TYPE)->getValue();
if (!$stream->test(\Twig_Token::PUNCTUATION_TYPE, ',')) {
break;
}
$stream->next();
}
$stream->expect(\Twig_Token::PUNCTUATION_TYPE, ']');
} elseif ($stream->test(\Twig_Token::NAME_TYPE, $this->extensions)) {
// an arbitrary configured attribute
$key = $stream->next()->getValue();
$stream->expect(\Twig_Token::OPERATOR_TYPE, '=');
$attributes[$key] = $stream->expect(\Twig_Token::STRING_TYPE)->getValue();
} else {
$token = $stream->getCurrent();
throw new \Twig_Error_Syntax(sprintf('Unexpected token "%s" of value "%s"', \Twig_Token::typeToEnglish($token->getType()), $token->getValue()), $token->getLine(), $stream->getFilename());
}
}
$stream->expect(\Twig_Token::BLOCK_END_TYPE);
$body = $this->parser->subparse(array($this, 'testEndTag'), true);
$stream->expect(\Twig_Token::BLOCK_END_TYPE);
if ($this->single && 1 < count($inputs)) {
$inputs = array_slice($inputs, -1);
}
if (!$name) {
$name = $this->factory->generateAssetName($inputs, $filters, $attributes);
}
$asset = $this->factory->createAsset($inputs, $filters, $attributes + array('name' => $name));
return $this->createBodyNode($asset, $body, $inputs, $filters, $name, $attributes, $token->getLine(), $this->getTag());
}
public function getTag()
{
return $this->tag;
}
public function testEndTag(\Twig_Token $token)
{
return $token->test(array('end'.$this->getTag()));
}
/**
* @param AssetInterface $asset
* @param \Twig_Node $body
* @param array $inputs
* @param array $filters
* @param string $name
* @param array $attributes
* @param int $lineno
* @param string $tag
*
* @return \Twig_Node
*/
protected function createBodyNode(AssetInterface $asset, \Twig_Node $body, array $inputs, array $filters, $name, array $attributes = array(), $lineno = 0, $tag = null)
{
$reflector = new \ReflectionMethod($this, 'createNode');
if (__CLASS__ !== $reflector->getDeclaringClass()->name) {
@trigger_error(sprintf('Overwriting %s::createNode is deprecated since 1.3. Overwrite %s instead.', __CLASS__, __METHOD__), E_USER_DEPRECATED);
return $this->createNode($asset, $body, $inputs, $filters, $name, $attributes, $lineno, $tag);
}
return new AsseticNode($asset, $body, $inputs, $filters, $name, $attributes, $lineno, $tag);
}
/**
* @param AssetInterface $asset
* @param \Twig_NodeInterface $body
* @param array $inputs
* @param array $filters
* @param string $name
* @param array $attributes
* @param int $lineno
* @param string $tag
*
* @return \Twig_Node
*
* @deprecated since 1.3.0, to be removed in 2.0. Use createBodyNode instead.
*/
protected function createNode(AssetInterface $asset, \Twig_NodeInterface $body, array $inputs, array $filters, $name, array $attributes = array(), $lineno = 0, $tag = null)
{
@trigger_error(sprintf('The %s method is deprecated since 1.3 and will be removed in 2.0. Use createBodyNode instead.', __METHOD__), E_USER_DEPRECATED);
if (!$body instanceof \Twig_Node) {
throw new \InvalidArgumentException('The body must be a Twig_Node. Custom implementations of Twig_NodeInterface are not supported.');
}
return new AsseticNode($asset, $body, $inputs, $filters, $name, $attributes, $lineno, $tag);
}
}
src/Assetic/Extension/Twig/TwigFormulaLoader.php 0000666 00000006776 13436750441 0015747 0 ustar 00
*/
class TwigFormulaLoader implements FormulaLoaderInterface
{
private $twig;
private $logger;
public function __construct(\Twig_Environment $twig, LoggerInterface $logger = null)
{
$this->twig = $twig;
$this->logger = $logger;
}
public function load(ResourceInterface $resource)
{
try {
$tokens = $this->twig->tokenize($resource->getContent(), (string) $resource);
$nodes = $this->twig->parse($tokens);
} catch (\Exception $e) {
if ($this->logger) {
$this->logger->error(sprintf('The template "%s" contains an error: %s', $resource, $e->getMessage()));
}
return array();
}
return $this->loadNode($nodes);
}
/**
* Loads assets from the supplied node.
*
* @param \Twig_Node $node
*
* @return array An array of asset formulae indexed by name
*/
private function loadNode(\Twig_Node $node)
{
$formulae = array();
if ($node instanceof AsseticNode) {
$formulae[$node->getAttribute('name')] = array(
$node->getAttribute('inputs'),
$node->getAttribute('filters'),
array(
'output' => $node->getAttribute('asset')->getTargetPath(),
'name' => $node->getAttribute('name'),
'debug' => $node->getAttribute('debug'),
'combine' => $node->getAttribute('combine'),
'vars' => $node->getAttribute('vars'),
),
);
} elseif ($node instanceof \Twig_Node_Expression_Function) {
$name = $node->getAttribute('name');
if ($this->twig->getFunction($name) instanceof AsseticFilterFunction) {
$arguments = array();
foreach ($node->getNode('arguments') as $argument) {
$arguments[] = eval('return '.$this->twig->compile($argument).';');
}
$invoker = $this->twig->getExtension('assetic')->getFilterInvoker($name);
$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'] = $invoker->getFactory()->generateAssetName($inputs, $filters, $options);
}
$formulae[$options['name']] = array($inputs, $filters, $options);
}
}
foreach ($node as $child) {
if ($child instanceof \Twig_Node) {
$formulae += $this->loadNode($child);
}
}
if ($node->hasAttribute('embedded_templates')) {
foreach ($node->getAttribute('embedded_templates') as $child) {
$formulae += $this->loadNode($child);
}
}
return $formulae;
}
}
src/Assetic/Extension/Twig/AsseticFilterInvoker.php 0000666 00000002473 13436750441 0016445 0 ustar 00
*/
class AsseticFilterInvoker
{
private $factory;
private $filters;
private $options;
public function __construct($factory, $filter)
{
$this->factory = $factory;
if (is_array($filter) && isset($filter['filter'])) {
$this->filters = (array) $filter['filter'];
$this->options = isset($filter['options']) ? (array) $filter['options'] : array();
} else {
$this->filters = (array) $filter;
$this->options = array();
}
}
public function getFactory()
{
return $this->factory;
}
public function getFilters()
{
return $this->filters;
}
public function getOptions()
{
return $this->options;
}
public function invoke($input, array $options = array())
{
$asset = $this->factory->createAsset($input, $this->filters, $options + $this->options);
return $asset->getTargetPath();
}
}
src/Assetic/Extension/Twig/AsseticExtension.php 0000666 00000004206 13436750441 0015632 0 ustar 00 factory = $factory;
$this->functions = array();
$this->valueSupplier = $valueSupplier;
foreach ($functions as $function => $options) {
if (is_integer($function) && is_string($options)) {
$this->functions[$options] = array('filter' => $options);
} else {
$this->functions[$function] = $options + array('filter' => $function);
}
}
}
public function getTokenParsers()
{
return array(
new AsseticTokenParser($this->factory, 'javascripts', 'js/*.js'),
new AsseticTokenParser($this->factory, 'stylesheets', 'css/*.css'),
new AsseticTokenParser($this->factory, 'image', 'images/*', true),
);
}
public function getFunctions()
{
$functions = array();
foreach ($this->functions as $function => $filter) {
$functions[] = new AsseticFilterFunction($function);
}
return $functions;
}
public function getGlobals()
{
return array(
'assetic' => array(
'debug' => $this->factory->isDebug(),
'vars' => null !== $this->valueSupplier ? new ValueContainer($this->valueSupplier) : array(),
),
);
}
public function getFilterInvoker($function)
{
return new AsseticFilterInvoker($this->factory, $this->functions[$function]);
}
public function getName()
{
return 'assetic';
}
}
src/Assetic/Extension/Twig/TwigResource.php 0000666 00000002170 13436750441 0014762 0 ustar 00
*/
class TwigResource implements ResourceInterface
{
private $loader;
private $name;
public function __construct(\Twig_LoaderInterface $loader, $name)
{
$this->loader = $loader;
$this->name = $name;
}
public function getContent()
{
try {
return $this->loader->getSource($this->name);
} catch (\Twig_Error_Loader $e) {
return '';
}
}
public function isFresh($timestamp)
{
try {
return $this->loader->isFresh($this->name, $timestamp);
} catch (\Twig_Error_Loader $e) {
return false;
}
}
public function __toString()
{
return $this->name;
}
}
src/Assetic/Extension/Twig/AsseticFilterNode.php 0000666 00000001143 13436750441 0015706 0 ustar 00 raw(sprintf('$this->env->getExtension(\'assetic\')->getFilterInvoker(\'%s\')->invoke', $this->getAttribute('name')));
$this->compileArguments($compiler);
}
}
composer.json 0000666 00000005662 13436750441 0007312 0 ustar 00 {
"name": "kriswallsmith/assetic",
"description": "Asset Management for PHP",
"keywords": [ "assets", "compression", "minification" ],
"homepage": "https://github.com/kriswallsmith/assetic",
"type": "library",
"license": "MIT",
"authors": [
{
"name": "Kris Wallsmith",
"email": "kris.wallsmith@gmail.com",
"homepage": "http://kriswallsmith.net/"
}
],
"require": {
"php": ">=5.3.1",
"symfony/process": "~2.1|~3.0"
},
"conflict": {
"twig/twig": "<1.23"
},
"require-dev": {
"phpunit/phpunit": "~4.8",
"symfony/phpunit-bridge": "~2.7|~3.0",
"twig/twig": "~1.8|~2.0",
"leafo/lessphp": "^0.3.7",
"leafo/scssphp": "~0.1",
"ptachoire/cssembed": "~1.0",
"cssmin/cssmin": "3.0.1",
"mrclay/minify": "~2.2",
"kamicane/packager": "1.0",
"joliclic/javascript-packer": "1.1",
"patchwork/jsqueeze": "~1.0|~2.0",
"psr/log": "~1.0"
},
"suggest": {
"twig/twig": "Assetic provides the integration with the Twig templating engine",
"leafo/lessphp": "Assetic provides the integration with the lessphp LESS compiler",
"leafo/scssphp": "Assetic provides the integration with the scssphp SCSS compiler",
"ptachoire/cssembed": "Assetic provides the integration with phpcssembed to embed data uris",
"leafo/scssphp-compass": "Assetic provides the integration with the SCSS compass plugin",
"patchwork/jsqueeze": "Assetic provides the integration with the JSqueeze JavaScript compressor"
},
"autoload": {
"psr-0": { "Assetic": "src/" },
"files": [ "src/functions.php" ]
},
"config": {
"bin-dir": "bin"
},
"extra": {
"branch-alias": {
"dev-master": "1.4-dev"
}
},
"repositories": [
{
"type": "package",
"package": {
"name": "cssmin/cssmin",
"version": "3.0.1",
"dist": { "url": "http://cssmin.googlecode.com/files/cssmin-v3.0.1.php", "type": "file" },
"autoload": { "classmap": [ "cssmin-v3.0.1.php" ] }
}
},
{
"type": "package",
"package": {
"name": "kamicane/packager",
"version": "1.0",
"dist": { "url": "https://github.com/kamicane/packager/archive/1.0.zip", "type": "zip" },
"autoload": { "classmap": [ "." ] }
}
},
{
"type": "package",
"package": {
"name": "joliclic/javascript-packer",
"version": "1.1",
"dist": { "url": "http://joliclic.free.fr/php/javascript-packer/telechargement.php?id=2&action=telecharger", "type": "zip" },
"autoload": { "classmap": [ "class.JavaScriptPacker.php" ] }
}
}
]
}