vendor/assetic/src/Assetic/Asset/AssetCollection.php
changeset 0 7f95f8617b0b
equal deleted inserted replaced
-1:000000000000 0:7f95f8617b0b
       
     1 <?php
       
     2 
       
     3 /*
       
     4  * This file is part of the Assetic package, an OpenSky project.
       
     5  *
       
     6  * (c) 2010-2011 OpenSky Project Inc
       
     7  *
       
     8  * For the full copyright and license information, please view the LICENSE
       
     9  * file that was distributed with this source code.
       
    10  */
       
    11 
       
    12 namespace Assetic\Asset;
       
    13 
       
    14 use Assetic\Filter\FilterCollection;
       
    15 use Assetic\Filter\FilterInterface;
       
    16 
       
    17 /**
       
    18  * A collection of assets.
       
    19  *
       
    20  * @author Kris Wallsmith <kris.wallsmith@gmail.com>
       
    21  */
       
    22 class AssetCollection implements AssetInterface, \IteratorAggregate
       
    23 {
       
    24     private $assets;
       
    25     private $filters;
       
    26     private $sourceRoot;
       
    27     private $targetPath;
       
    28     private $content;
       
    29     private $clones;
       
    30 
       
    31     /**
       
    32      * Constructor.
       
    33      *
       
    34      * @param array  $assets     Assets for the current collection
       
    35      * @param array  $filters    Filters for the current collection
       
    36      * @param string $sourceRoot The root directory
       
    37      */
       
    38     public function __construct($assets = array(), $filters = array(), $sourceRoot = null)
       
    39     {
       
    40         $this->assets = array();
       
    41         foreach ($assets as $asset) {
       
    42             $this->add($asset);
       
    43         }
       
    44 
       
    45         $this->filters = new FilterCollection($filters);
       
    46         $this->sourceRoot = $sourceRoot;
       
    47         $this->clones = new \SplObjectStorage();
       
    48     }
       
    49 
       
    50     /**
       
    51      * Adds an asset to the current collection.
       
    52      *
       
    53      * @param AssetInterface $asset An asset
       
    54      */
       
    55     public function add(AssetInterface $asset)
       
    56     {
       
    57         $this->assets[] = $asset;
       
    58     }
       
    59 
       
    60     public function all()
       
    61     {
       
    62         return $this->assets;
       
    63     }
       
    64 
       
    65     public function ensureFilter(FilterInterface $filter)
       
    66     {
       
    67         $this->filters->ensure($filter);
       
    68     }
       
    69 
       
    70     public function getFilters()
       
    71     {
       
    72         return $this->filters->all();
       
    73     }
       
    74 
       
    75     public function clearFilters()
       
    76     {
       
    77         $this->filters->clear();
       
    78     }
       
    79 
       
    80     public function load(FilterInterface $additionalFilter = null)
       
    81     {
       
    82         // loop through leaves and load each asset
       
    83         $parts = array();
       
    84         foreach ($this as $asset) {
       
    85             $asset->load($additionalFilter);
       
    86             $parts[] = $asset->getContent();
       
    87         }
       
    88 
       
    89         $this->content = implode("\n", $parts);
       
    90     }
       
    91 
       
    92     public function dump(FilterInterface $additionalFilter = null)
       
    93     {
       
    94         // loop through leaves and dump each asset
       
    95         $parts = array();
       
    96         foreach ($this as $asset) {
       
    97             $parts[] = $asset->dump($additionalFilter);
       
    98         }
       
    99 
       
   100         return implode("\n", $parts);
       
   101     }
       
   102 
       
   103     public function getContent()
       
   104     {
       
   105         return $this->content;
       
   106     }
       
   107 
       
   108     public function setContent($content)
       
   109     {
       
   110         $this->content = $content;
       
   111     }
       
   112 
       
   113     public function getSourceRoot()
       
   114     {
       
   115         return $this->sourceRoot;
       
   116     }
       
   117 
       
   118     public function getSourcePath()
       
   119     {
       
   120     }
       
   121 
       
   122     public function getTargetPath()
       
   123     {
       
   124         return $this->targetPath;
       
   125     }
       
   126 
       
   127     public function setTargetPath($targetPath)
       
   128     {
       
   129         $this->targetPath = $targetPath;
       
   130     }
       
   131 
       
   132     /**
       
   133      * Returns the highest last-modified value of all assets in the current collection.
       
   134      *
       
   135      * @return integer|null A UNIX timestamp
       
   136      */
       
   137     public function getLastModified()
       
   138     {
       
   139         if (!count($this->assets)) {
       
   140             return;
       
   141         }
       
   142 
       
   143         $mapper = function (AssetInterface $asset)
       
   144         {
       
   145             return $asset->getLastModified();
       
   146         };
       
   147 
       
   148         return max(array_map($mapper, $this->assets));
       
   149     }
       
   150 
       
   151     /**
       
   152      * Returns an iterator for looping recursively over unique leaves.
       
   153      */
       
   154     public function getIterator()
       
   155     {
       
   156         return new \RecursiveIteratorIterator(new AssetCollectionFilterIterator(new AssetCollectionIterator($this, $this->clones)));
       
   157     }
       
   158 }
       
   159 
       
   160 /**
       
   161  * Asset collection filter iterator.
       
   162  *
       
   163  * The filter iterator is responsible for de-duplication of leaf assets based
       
   164  * on both strict equality and source URL.
       
   165  *
       
   166  * @author Kris Wallsmith <kris.wallsmith@gmail.com>
       
   167  * @access private
       
   168  */
       
   169 class AssetCollectionFilterIterator extends \RecursiveFilterIterator
       
   170 {
       
   171     private $visited;
       
   172     private $sources;
       
   173 
       
   174     /**
       
   175      * Constructor.
       
   176      *
       
   177      * @param AssetCollectionIterator $iterator The inner iterator
       
   178      * @param array                   $visited  An array of visited asset objects
       
   179      * @param array                   $sources  An array of visited source strings
       
   180      */
       
   181     public function __construct(AssetCollectionIterator $iterator, array $visited = array(), array $sources = array())
       
   182     {
       
   183         parent::__construct($iterator);
       
   184 
       
   185         $this->visited = $visited;
       
   186         $this->sources = $sources;
       
   187     }
       
   188 
       
   189     /**
       
   190      * Determines whether the current asset is a duplicate.
       
   191      *
       
   192      * De-duplication is performed based on either strict equality or by
       
   193      * matching sources.
       
   194      *
       
   195      * @return Boolean Returns true if we have not seen this asset yet
       
   196      */
       
   197     public function accept()
       
   198     {
       
   199         $asset = $this->getInnerIterator()->current(true);
       
   200         $duplicate = false;
       
   201 
       
   202         // check strict equality
       
   203         if (in_array($asset, $this->visited, true)) {
       
   204             $duplicate = true;
       
   205         } else {
       
   206             $this->visited[] = $asset;
       
   207         }
       
   208 
       
   209         // check source
       
   210         $sourceRoot = $asset->getSourceRoot();
       
   211         $sourcePath = $asset->getSourcePath();
       
   212         if ($sourceRoot && $sourcePath) {
       
   213             $source = $sourceRoot.'/'.$sourcePath;
       
   214             if (in_array($source, $this->sources)) {
       
   215                 $duplicate = true;
       
   216             } else {
       
   217                 $this->sources[] = $source;
       
   218             }
       
   219         }
       
   220 
       
   221         return !$duplicate;
       
   222     }
       
   223 
       
   224     /**
       
   225      * Passes visited objects and source URLs to the child iterator.
       
   226      */
       
   227     public function getChildren()
       
   228     {
       
   229         return new self($this->getInnerIterator()->getChildren(), $this->visited, $this->sources);
       
   230     }
       
   231 }
       
   232 
       
   233 /**
       
   234  * Iterates over an asset collection.
       
   235  *
       
   236  * The iterator is responsible for cascading filters and target URL patterns
       
   237  * from parent to child assets.
       
   238  *
       
   239  * @author Kris Wallsmith <kris.wallsmith@gmail.com>
       
   240  * @access private
       
   241  */
       
   242 class AssetCollectionIterator implements \RecursiveIterator
       
   243 {
       
   244     private $assets;
       
   245     private $filters;
       
   246     private $output;
       
   247     private $clones;
       
   248 
       
   249     public function __construct(AssetCollection $coll, \SplObjectStorage $clones)
       
   250     {
       
   251         $this->assets  = $coll->all();
       
   252         $this->filters = $coll->getFilters();
       
   253         $this->output  = $coll->getTargetPath();
       
   254         $this->clones  = $clones;
       
   255 
       
   256         if (false === $pos = strpos($this->output, '.')) {
       
   257             $this->output .= '_*';
       
   258         } else {
       
   259             $this->output = substr($this->output, 0, $pos).'_*'.substr($this->output, $pos);
       
   260         }
       
   261     }
       
   262 
       
   263     /**
       
   264      * Returns a copy of the current asset with filters and a target URL applied.
       
   265      *
       
   266      * @param Boolean $raw Returns the unmodified asset if true
       
   267      */
       
   268     public function current($raw = false)
       
   269     {
       
   270         $asset = current($this->assets);
       
   271 
       
   272         if ($raw) {
       
   273             return $asset;
       
   274         }
       
   275 
       
   276         // clone once
       
   277         if (!isset($this->clones[$asset])) {
       
   278             $clone = $this->clones[$asset] = clone $asset;
       
   279 
       
   280             // generate a target path based on asset name
       
   281             $name = sprintf('%s_%d', pathinfo($asset->getSourcePath(), PATHINFO_FILENAME) ?: 'part', $this->key() + 1);
       
   282             $clone->setTargetPath(str_replace('*', $name, $this->output));
       
   283         } else {
       
   284             $clone = $this->clones[$asset];
       
   285         }
       
   286 
       
   287         // cascade filters
       
   288         foreach ($this->filters as $filter) {
       
   289             $clone->ensureFilter($filter);
       
   290         }
       
   291 
       
   292         return $clone;
       
   293     }
       
   294 
       
   295     public function key()
       
   296     {
       
   297         return key($this->assets);
       
   298     }
       
   299 
       
   300     public function next()
       
   301     {
       
   302         return next($this->assets);
       
   303     }
       
   304 
       
   305     public function rewind()
       
   306     {
       
   307         return reset($this->assets);
       
   308     }
       
   309 
       
   310     public function valid()
       
   311     {
       
   312         return false !== current($this->assets);
       
   313     }
       
   314 
       
   315     public function hasChildren()
       
   316     {
       
   317         return current($this->assets) instanceof AssetCollection;
       
   318     }
       
   319 
       
   320     /**
       
   321      * @uses current()
       
   322      */
       
   323     public function getChildren()
       
   324     {
       
   325         return new self($this->current(), $this->clones);
       
   326     }
       
   327 }