vendor/bundles/Symfony/Bundle/AsseticBundle/Command/DumpCommand.php
changeset 0 7f95f8617b0b
equal deleted inserted replaced
-1:000000000000 0:7f95f8617b0b
       
     1 <?php
       
     2 
       
     3 /*
       
     4  * This file is part of the Symfony framework.
       
     5  *
       
     6  * (c) Fabien Potencier <fabien@symfony.com>
       
     7  *
       
     8  * This source file is subject to the MIT license that is bundled
       
     9  * with this source code in the file LICENSE.
       
    10  */
       
    11 
       
    12 namespace Symfony\Bundle\AsseticBundle\Command;
       
    13 
       
    14 use Assetic\Asset\AssetInterface;
       
    15 use Assetic\Factory\LazyAssetManager;
       
    16 use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
       
    17 use Symfony\Component\Console\Input\InputArgument;
       
    18 use Symfony\Component\Console\Input\InputInterface;
       
    19 use Symfony\Component\Console\Input\InputOption;
       
    20 use Symfony\Component\Console\Output\OutputInterface;
       
    21 
       
    22 /**
       
    23  * Dumps assets to the filesystem.
       
    24  *
       
    25  * @author Kris Wallsmith <kris@symfony.com>
       
    26  */
       
    27 class DumpCommand extends ContainerAwareCommand
       
    28 {
       
    29     private $basePath;
       
    30     private $verbose;
       
    31     private $am;
       
    32 
       
    33     protected function configure()
       
    34     {
       
    35         $this
       
    36             ->setName('assetic:dump')
       
    37             ->setDescription('Dumps all assets to the filesystem')
       
    38             ->addArgument('write_to', InputArgument::OPTIONAL, 'Override the configured asset root')
       
    39             ->addOption('watch', null, InputOption::VALUE_NONE, 'Check for changes every second, debug mode only')
       
    40             ->addOption('force', null, InputOption::VALUE_NONE, 'Force an initial generation of all assets (used with --watch)')
       
    41             ->addOption('period', null, InputOption::VALUE_REQUIRED, 'Set the polling period in seconds (used with --watch)', 1)
       
    42         ;
       
    43     }
       
    44 
       
    45     protected function initialize(InputInterface $input, OutputInterface $output)
       
    46     {
       
    47         parent::initialize($input, $output);
       
    48 
       
    49         $this->basePath = $input->getArgument('write_to') ?: $this->getContainer()->getParameter('assetic.write_to');
       
    50         $this->verbose = $input->getOption('verbose');
       
    51         $this->am = $this->getContainer()->get('assetic.asset_manager');
       
    52     }
       
    53 
       
    54     protected function execute(InputInterface $input, OutputInterface $output)
       
    55     {
       
    56         $output->writeln(sprintf('Dumping all <comment>%s</comment> assets.', $input->getOption('env')));
       
    57         $output->writeln(sprintf('Debug mode is <comment>%s</comment>.', $input->getOption('no-debug') ? 'off' : 'on'));
       
    58         $output->writeln('');
       
    59 
       
    60         if (!$input->getOption('watch')) {
       
    61             foreach ($this->am->getNames() as $name) {
       
    62                 $this->dumpAsset($name, $output);
       
    63             }
       
    64 
       
    65             return;
       
    66         }
       
    67 
       
    68         if (!$this->am->isDebug()) {
       
    69             throw new \RuntimeException('The --watch option is only available in debug mode.');
       
    70         }
       
    71 
       
    72         $this->watch($input, $output);
       
    73     }
       
    74 
       
    75     /**
       
    76      * Watches a asset manager for changes.
       
    77      *
       
    78      * This method includes an infinite loop the continuously polls the asset
       
    79      * manager for changes.
       
    80      *
       
    81      * @param InputInterface  $input  The command input
       
    82      * @param OutputInterface $output The command output
       
    83      */
       
    84     private function watch(InputInterface $input, OutputInterface $output)
       
    85     {
       
    86         $refl = new \ReflectionClass('Assetic\\AssetManager');
       
    87         $prop = $refl->getProperty('assets');
       
    88         $prop->setAccessible(true);
       
    89 
       
    90         $cache = sys_get_temp_dir().'/assetic_watch_'.substr(sha1($this->basePath), 0, 7);
       
    91         if ($input->getOption('force') || !file_exists($cache)) {
       
    92             $previously = array();
       
    93         } else {
       
    94             $previously = unserialize(file_get_contents($cache));
       
    95         }
       
    96 
       
    97         $error = '';
       
    98         while (true) {
       
    99             try {
       
   100                 foreach ($this->am->getNames() as $name) {
       
   101                     if ($this->checkAsset($name, $previously)) {
       
   102                         $this->dumpAsset($name, $output);
       
   103                     }
       
   104                 }
       
   105 
       
   106                 // reset the asset manager
       
   107                 $prop->setValue($this->am, array());
       
   108                 $this->am->load();
       
   109 
       
   110                 file_put_contents($cache, serialize($previously));
       
   111                 $error = '';
       
   112 
       
   113                 sleep($input->getOption('period'));
       
   114             } catch (\Exception $e) {
       
   115                 if ($error != $msg = $e->getMessage()) {
       
   116                     $output->writeln('<error>[error]</error> '.$msg);
       
   117                     $error = $msg;
       
   118                 }
       
   119             }
       
   120         }
       
   121     }
       
   122 
       
   123     /**
       
   124      * Checks if an asset should be dumped.
       
   125      *
       
   126      * @param string $name        The asset name
       
   127      * @param array  &$previously An array of previous visits
       
   128      *
       
   129      * @return Boolean Whether the asset should be dumped
       
   130      */
       
   131     private function checkAsset($name, array &$previously)
       
   132     {
       
   133         $formula = $this->am->hasFormula($name) ? serialize($this->am->getFormula($name)) : null;
       
   134         $asset = $this->am->get($name);
       
   135         $mtime = $asset->getLastModified();
       
   136 
       
   137         if (isset($previously[$name])) {
       
   138             $changed = $previously[$name]['mtime'] != $mtime || $previously[$name]['formula'] != $formula;
       
   139         } else {
       
   140             $changed = true;
       
   141         }
       
   142 
       
   143         $previously[$name] = array('mtime' => $mtime, 'formula' => $formula);
       
   144 
       
   145         return $changed;
       
   146     }
       
   147 
       
   148     /**
       
   149      * Writes an asset.
       
   150      *
       
   151      * If the application or asset is in debug mode, each leaf asset will be
       
   152      * dumped as well.
       
   153      *
       
   154      * @param string          $name   An asset name
       
   155      * @param OutputInterface $output The command output
       
   156      */
       
   157     private function dumpAsset($name, OutputInterface $output)
       
   158     {
       
   159         $asset = $this->am->get($name);
       
   160         $formula = $this->am->getFormula($name);
       
   161 
       
   162         // start by dumping the main asset
       
   163         $this->doDump($asset, $output);
       
   164 
       
   165         // dump each leaf if debug
       
   166         if (isset($formula[2]['debug']) ? $formula[2]['debug'] : $this->am->isDebug()) {
       
   167             foreach ($asset as $leaf) {
       
   168                 $this->doDump($leaf, $output);
       
   169             }
       
   170         }
       
   171     }
       
   172 
       
   173     /**
       
   174      * Performs the asset dump.
       
   175      *
       
   176      * @param AssetInterface  $asset  An asset
       
   177      * @param OutputInterface $output The command output
       
   178      *
       
   179      * @throws RuntimeException If there is a problem writing the asset
       
   180      */
       
   181     private function doDump(AssetInterface $asset, OutputInterface $output)
       
   182     {
       
   183         $target = rtrim($this->basePath, '/').'/'.str_replace('_controller/', '', $asset->getTargetPath());
       
   184         if (!is_dir($dir = dirname($target))) {
       
   185             $output->writeln('<info>[dir+]</info>  '.$dir);
       
   186             if (false === @mkdir($dir, 0777, true)) {
       
   187                 throw new \RuntimeException('Unable to create directory '.$dir);
       
   188             }
       
   189         }
       
   190 
       
   191         $output->writeln('<info>[file+]</info> '.$target);
       
   192         if ($this->verbose) {
       
   193             if ($asset instanceof \Traversable) {
       
   194                 foreach ($asset as $leaf) {
       
   195                     $root = $leaf->getSourceRoot();
       
   196                     $path = $leaf->getSourcePath();
       
   197                     $output->writeln(sprintf('        <comment>%s/%s</comment>', $root ?: '[unknown root]', $path ?: '[unknown path]'));
       
   198                 }
       
   199             } else {
       
   200                 $root = $asset->getSourceRoot();
       
   201                 $path = $asset->getSourcePath();
       
   202                 $output->writeln(sprintf('        <comment>%s/%s</comment>', $root ?: '[unknown root]', $path ?: '[unknown path]'));
       
   203             }
       
   204         }
       
   205 
       
   206         if (false === @file_put_contents($target, $asset->dump())) {
       
   207             throw new \RuntimeException('Unable to write file '.$target);
       
   208         }
       
   209     }
       
   210 }