DumpCommand.php 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  1. <?php
  2. /*
  3. * This file is part of the Symfony framework.
  4. *
  5. * (c) Fabien Potencier <fabien@symfony.com>
  6. *
  7. * This source file is subject to the MIT license that is bundled
  8. * with this source code in the file LICENSE.
  9. */
  10. namespace Symfony\Bundle\AsseticBundle\Command;
  11. use Assetic\Asset\AssetInterface;
  12. use Assetic\Factory\LazyAssetManager;
  13. use Symfony\Bundle\FrameworkBundle\Command\Command;
  14. use Symfony\Component\Console\Input\InputArgument;
  15. use Symfony\Component\Console\Input\InputInterface;
  16. use Symfony\Component\Console\Input\InputOption;
  17. use Symfony\Component\Console\Output\OutputInterface;
  18. /**
  19. * Dumps assets to the filesystem.
  20. *
  21. * @author Kris Wallsmith <kris.wallsmith@symfony.com>
  22. */
  23. class DumpCommand extends Command
  24. {
  25. protected function configure()
  26. {
  27. $this
  28. ->setName('assetic:dump')
  29. ->setDescription('Dumps all assets to the filesystem')
  30. ->addArgument('write_to', InputArgument::OPTIONAL, 'Override the configured asset root')
  31. ->addOption('watch', null, InputOption::VALUE_NONE, 'Check for changes every second, debug mode only')
  32. ;
  33. }
  34. protected function execute(InputInterface $input, OutputInterface $output)
  35. {
  36. if (!$basePath = $input->getArgument('write_to')) {
  37. $basePath = $this->container->getParameter('assetic.write_to');
  38. }
  39. $am = $this->container->get('assetic.asset_manager');
  40. if ($input->getOption('watch')) {
  41. return $this->watch($am, $basePath, $output, $this->container->getParameter('kernel.debug'));
  42. }
  43. foreach ($am->getNames() as $name) {
  44. $this->dumpAsset($am->get($name), $basePath, $output);
  45. }
  46. }
  47. /**
  48. * Watches a asset manager for changes.
  49. *
  50. * This method includes an infinite loop the continuously polls the asset
  51. * manager for changes.
  52. *
  53. * @param LazyAssetManager $am The asset manager
  54. * @param string $basePath The base directory to write to
  55. * @param OutputInterface $output The command output
  56. * @param Boolean $debug Debug mode
  57. */
  58. protected function watch(LazyAssetManager $am, $basePath, OutputInterface $output, $debug = false)
  59. {
  60. if (!$debug) {
  61. throw new \RuntimeException('The --watch option is only available in debug mode.');
  62. }
  63. $refl = new \ReflectionClass('Assetic\\AssetManager');
  64. $prop = $refl->getProperty('assets');
  65. $prop->setAccessible(true);
  66. $cache = sys_get_temp_dir().'/assetic_watch_'.substr(sha1($basePath), 0, 7);
  67. if (file_exists($cache)) {
  68. $previously = unserialize(file_get_contents($cache));
  69. } else {
  70. $previously = array();
  71. }
  72. $error = '';
  73. while (true) {
  74. try {
  75. foreach ($am->getNames() as $name) {
  76. if ($asset = $this->checkAsset($am, $name, $previously)) {
  77. $this->dumpAsset($asset, $basePath, $output);
  78. }
  79. }
  80. // reset the asset manager
  81. $prop->setValue($am, array());
  82. $am->load();
  83. file_put_contents($cache, serialize($previously));
  84. $error = '';
  85. sleep(1);
  86. } catch (\Exception $e) {
  87. if ($error != $msg = $e->getMessage()) {
  88. $output->writeln('<error>[error]</error> '.$msg);
  89. $error = $msg;
  90. }
  91. }
  92. }
  93. }
  94. /**
  95. * Checks if an asset should be dumped.
  96. *
  97. * @param LazyAssetManager $am The asset manager
  98. * @param string $name The asset name
  99. * @param array $previously An array of previous visits
  100. *
  101. * @return AssetInterface|Boolean The asset if it should be dumped
  102. */
  103. protected function checkAsset(LazyAssetManager $am, $name, array &$previously)
  104. {
  105. $formula = $am->hasFormula($name) ? serialize($am->getFormula($name)) : null;
  106. $asset = $am->get($name);
  107. $mtime = $asset->getLastModified();
  108. if (isset($previously[$name])) {
  109. $changed = $previously[$name]['mtime'] != $mtime || $previously[$name]['formula'] != $formula;
  110. } else {
  111. $changed = true;
  112. }
  113. $previously[$name] = array('mtime' => $mtime, 'formula' => $formula);
  114. return $changed ? $asset : false;
  115. }
  116. /**
  117. * Writes an asset.
  118. *
  119. * @param AssetInterface $asset An asset
  120. * @param string $basePath The base directory to write to
  121. * @param OutputInterface $output The command output
  122. *
  123. * @throws RuntimeException If there is a problem writing the asset
  124. */
  125. protected function dumpAsset(AssetInterface $asset, $basePath, OutputInterface $output)
  126. {
  127. $target = rtrim($basePath, '/') . '/' . $asset->getTargetUrl();
  128. if (!is_dir($dir = dirname($target))) {
  129. $output->writeln('<info>[dir+]</info> '.$dir);
  130. if (false === @mkdir($dir, 0777, true)) {
  131. throw new \RuntimeException('Unable to create directory '.$dir);
  132. }
  133. }
  134. $output->writeln('<info>[file+]</info> '.$target);
  135. if (false === @file_put_contents($target, $asset->dump())) {
  136. throw new \RuntimeException('Unable to write file '.$target);
  137. }
  138. }
  139. }