Don't like ads? PRO users don't see any ads ;-)
Guest

Untitled

By: a guest on Jun 2nd, 2012  |  syntax: None  |  size: 12.52 KB  |  hits: 14  |  expires: Never
download  |  raw  |  embed  |  report abuse  |  print
Text below is selected. Please press Ctrl+C to copy to your clipboard. (⌘+C on Mac)
  1. <?php
  2.  
  3. namespace Application\CommonBundle\Command;
  4.  
  5. use Symfony\Component\Console\Input\InputInterface;
  6. use Symfony\Component\Console\Output\OutputInterface;
  7. use Symfony\Component\Console\Input\InputOption;
  8.  
  9. use Symfony\Component\Console\Input\ArrayInput;
  10. use Symfony\Component\Console\Output\NullOutput;
  11.  
  12. use Assetic\Asset\AssetInterface;
  13.  
  14. /**
  15.  * Command to setup application assets in one step
  16.  *
  17.  */
  18. class SetupAssetsCommand extends Command
  19. {
  20.  
  21.     private $_asseticManager;
  22.     private $_webDir;  
  23.    
  24.     protected function configure()
  25.     {
  26.         parent::configure();
  27.  
  28.         $this->setName('tracpoint:assets')
  29.             ->setDescription('Setup system assets & assetic')
  30.             ->addOption('warm', null, InputOption::VALUE_NONE, 'Warm up the cache before doing any operations')                
  31.             ->addOption('watch', null, InputOption::VALUE_NONE, 'Check for changes every second, debug mode only')
  32.             ->addOption('force', null, InputOption::VALUE_NONE, 'Force an initial generation of all assets (used with --watch)')
  33.             ->addOption('period', null, InputOption::VALUE_REQUIRED, 'Set the polling period in seconds (used with --watch)', 1)                
  34.             ->setHelp(<<<EOT
  35. The only reason for this command is a current limitation of the assetic issue described here:
  36.    
  37.     https://github.com/kriswallsmith/assetic/issues/53
  38.    
  39. Because of this assetic is reading the files not directly from the Resources directory in each
  40. bundle, but from the installed assets. This forces us to install the assets everytime before
  41. telling assetic to update the compiled assets.
  42.  
  43. The <info>tracpoint:assets</info> command prepares all public assets to be ready to be served.
  44.    
  45. It fist installs all the assets from the bundles directories and then compiles the corresponding assetic files.
  46.  
  47. When the --watch option is used, the command will monitor the original resource files and when it detects
  48. a change it will install its asset and then compile them with assetic.
  49.  
  50. EOT
  51.         );
  52.     }
  53.  
  54.     protected function initialize(InputInterface $input, OutputInterface $output)
  55.     {
  56.         parent::initialize($input, $output);
  57.        
  58.         $this->_webDir = $webDir = realpath(__DIR__ . '/../../../../web');  
  59.         $this->_asseticManager = $this->getContainer()->get('assetic.asset_manager');
  60.     }
  61.    
  62.     protected function execute(InputInterface $input, OutputInterface $output)
  63.     {          
  64.         $env = $this->getContainer()->getParameter('kernel.environment');
  65.  
  66.         $output->write("Running in Application Environment: $env", true);
  67.         $output->write('',true);
  68.                
  69.         // We need to clear the cache because assetic bundle metadadata is there
  70.         if ($input->getOption('warm')) {        
  71.             $output->write('-> Warming up the cache to refresh assetic metadata...');
  72.             $tmpInput = new ArrayInput(array('cache:warmup'));    
  73.             $this->getApplication()->doRun($tmpInput, new NullOutput);
  74.             $output->write('OK.',true);
  75.         }
  76.  
  77.         if ($input->getOption('watch')) {
  78.             $output->write('-> Watcher booting', true);
  79.             $this->_watch($input, $output);
  80.             return 0;
  81.         }  
  82.        
  83.         $webDir = $this->_webDir;
  84.         $output->write("-> Installing assets to '$webDir' ...", true);        
  85.         $tmpInput = new ArrayInput(array('assets:install','target'=>$webDir));    
  86.         $this->getApplication()->doRun($tmpInput, $output);
  87.        
  88.         $output->write('-> Dumping assetic bundle files...', true);
  89.         $tmpInput = new ArrayInput(array('assetic:dump'));    
  90.         $this->getApplication()->doRun($tmpInput, $output);        
  91.        
  92.         $output->write('',true);
  93.         $output->write('Ready.', true);        
  94.              
  95.     }    
  96.    
  97.     private function _watch(InputInterface $input, OutputInterface $output)
  98.     {
  99.        
  100.         $cacheFileName = sys_get_temp_dir() . 'tracpoint_assets_watch.cache';
  101.        
  102.         //If true all assets will be regenerated on the first cycle
  103.         $forceRefreshFirstTime = false;
  104.        
  105.         // Array with cached files modification time
  106.         $timestamps = array();
  107.        
  108.         // Try to load cache
  109.         if (!$input->getOption('force') && file_exists($cacheFileName)) {
  110.             $output->write(" Timestamps cached. Loading '$cacheFileName' ...");            
  111.             $timestamps = @unserialize(file_get_contents($cacheFileName));
  112.             if (!$timestamps) {
  113.                 $output->write("Error - resetting cache.", true);            
  114.                 $timestamps = array();
  115.             }else {
  116.                 $output->write("Loaded.", true);
  117.             }
  118.         }
  119.                
  120.         if (!count($timestamps)) {
  121.             $output->write("New timestamp cache: Saving cache of files modified time to '$cacheFileName'", true);
  122.             $output->write('', true);
  123.             $timestamps = array();
  124.             $forceRefreshFirstTime = true;
  125.         }      
  126.  
  127.        
  128.         if ($forceRefreshFirstTime) {
  129.             $output->write("Processing all assets for first time: ", true);
  130.         }
  131.         else {
  132.            $output->write('Now waiting for changes...',true);
  133.         }
  134.        
  135.         $error = '';
  136.         while (true) {
  137.             try {
  138.                 foreach ($this->getContainer()->get('kernel')->getBundles() as $bundle) {
  139.  
  140.                     $resourcePath = $this->_getBundleResourcesPath($bundle);
  141.  
  142.                     if (!is_dir($resourcePath)) {
  143.                         continue;
  144.                     }        
  145.  
  146.                     $finder = new \Symfony\Component\Finder\Finder();        
  147.                     $finder->files()->in($resourcePath);
  148.                     foreach($finder as $file) {
  149.  
  150.                         if (!$forceRefreshFirstTime && !$this->_assetShouldBeUpdated($file, $timestamps)) {
  151.                             continue;
  152.                         }
  153.  
  154.                         if ($forceRefreshFirstTime) {
  155.                             $output->write('.');
  156.                         }
  157.                         else {
  158.                             $output->write('',true);
  159.                             $output->write(" -> Change detected on {$file->getRealPath()}", true);
  160.                             $output->write("  * Updating asset", true);
  161.                         }
  162.                         $installedAssetPath = $this->_installAsset($bundle, $file, $timestamps, $output);          
  163.                        
  164.                         if (!$forceRefreshFirstTime) {
  165.                             $output->write("  * Updating corresponding assetic files...");
  166.                         }
  167.                        
  168.                         $assetDestinationRelativePath = ltrim(str_replace($this->_webDir, '', $installedAssetPath),'\\/');
  169.                         if (!$this->_installAsseticForMatchingAsset($assetDestinationRelativePath)) {
  170.                             $asseticCompiled = false;
  171.                         }
  172.                         else {
  173.                             $asseticCompiled = true;
  174.                         }
  175.  
  176.                         if (!$asseticCompiled && !$forceRefreshFirstTime) {
  177.                             $output->write('',true);
  178.                             $output->write("  *** WARNING *** No assetic data found for this asset, is it new? - recreating the cache may help", true);
  179.                         }
  180.                         elseif (!$forceRefreshFirstTime) {
  181.                             $output->write('OK.',true);
  182.                         }
  183.                        
  184.                         @file_put_contents($cacheFileName, serialize($timestamps));
  185.                     }
  186.                 }        
  187.                
  188.                 if ($forceRefreshFirstTime) {                
  189.                     $output->write('Finished!',true);
  190.                     $output->write('Now waiting for changes...',true);
  191.                     $forceRefreshFirstTime = false;
  192.                 }
  193.                
  194.                 sleep($input->getOption('period'));
  195.                
  196.             } catch (\Exception $e) {
  197.                 if ($error != $msg = $e->getMessage()) {
  198.                     $output->writeln('<error>[error]</error> ' . $msg);
  199.                     $error = $msg;
  200.                 }
  201.             }
  202.         }
  203.     }
  204.        
  205.  
  206.     private function _assetShouldBeUpdated($asset, array &$cachedTimestamps)
  207.     {
  208.         $assetHash = $this->_getAssetHash($asset);
  209.         if (isset($cachedTimestamps[$assetHash])) {
  210.             $cacheTime = $cachedTimestamps[$assetHash];
  211.         }
  212.         else {
  213.             $cacheTime = false;
  214.         }                
  215.  
  216.         $mtime = filemtime($this->_getAssetFullPath($asset));
  217.         if (!$cacheTime || $cacheTime < $mtime) {
  218.             return true;
  219.         }
  220.        
  221.         return false;
  222.     }
  223.        
  224.     private function _installAsset($bundle, $asset, array &$cachedTimestamps, OutputInterface $output)
  225.     {
  226.         $assetFullPath = $this->_getAssetFullPath($asset);
  227.  
  228.         // Bundle resources directory
  229.         $bundleResourcesPath = $this->_getBundleResourcesPath($bundle);                
  230.        
  231.         // Get the asset relative path form bundle resources directory
  232.         $assetRelativePath = str_replace($bundleResourcesPath . '/' , '', $assetFullPath);        
  233.        
  234.         $bundleName = preg_replace('/bundle$/', '', strtolower($bundle->getName()));
  235.         $assetDestinationRelativePath = 'bundles/' . $bundleName . '/' . $assetRelativePath;
  236.        
  237.         $destination = rtrim($this->_webDir,'\\/') . '/' . $assetDestinationRelativePath;
  238.        
  239.         $filesystem = $this->getContainer()->get('filesystem');
  240.         $filesystem->mkdir(dirname($assetDestinationRelativePath), 0777);
  241.                                    
  242.         if (!@copy($assetFullPath, $destination)) {
  243.             throw new \Exception("Error copying $assetFullPath to $destination");
  244.         }
  245.                
  246.         // Update cache
  247.         $assetHash = $this->_getAssetHash($asset);
  248.         $cachedTimestamps[$assetHash] = filemtime($assetFullPath);
  249.        
  250.         return $destination;
  251.     }
  252.    
  253.     private function _installAsseticForMatchingAsset($assetDestinationRelativePath)
  254.     {
  255.         foreach ($this->_asseticManager->getNames() as $name) {
  256.  
  257.             $assets = $this->_asseticManager->get($name)->all();
  258.             foreach($assets as $asset) {                
  259.                 if ($asset->getSourcePath() == $assetDestinationRelativePath) {
  260.                     $this->_compileAssetic($name);
  261.                     return true;
  262.                 }
  263.             }
  264.         }    
  265.        
  266.         return false;
  267.     }
  268.    
  269.     /**
  270.      * Writes an assetic asset.
  271.      *
  272.      * If the application or asset is in debug mode, each leaf asset will be
  273.      * dumped as well.
  274.      *
  275.      * @param string          $name   An asset name
  276.      * @param OutputInterface $output The command output
  277.      */
  278.     private function _compileAssetic($name)
  279.     {
  280.         $asset = $this->_asseticManager->get($name);
  281.         $formula = $this->_asseticManager->getFormula($name);
  282.  
  283.         // start by dumping the main asset
  284.         $this->_doCompileAssetic($asset);
  285.  
  286.         // dump each leaf if debug
  287.         if (isset($formula[2]['debug']) ? $formula[2]['debug'] : $this->_asseticManager->isDebug()) {
  288.             foreach ($asset as $leaf) {
  289.                 $this->_doCompileAssetic($leaf);
  290.             }
  291.         }
  292.     }
  293.    
  294.  
  295.     /**
  296.      * Performs the assetic asset dump.
  297.      *
  298.      * @param AssetInterface  $asset  An asset
  299.      * @param OutputInterface $output The command output
  300.      *
  301.      * @throws RuntimeException If there is a problem writing the asset
  302.      */
  303.     private function _doCompileAssetic(AssetInterface $asset)
  304.     {
  305.         $asseticBasePath = $this->getContainer()->getParameter('assetic.write_to');
  306.         $target = rtrim($asseticBasePath, '/').'/'.str_replace('_controller/', '', $asset->getTargetPath());
  307.        
  308.         if (!is_dir($dir = dirname($target))) {
  309.             if (false === @mkdir($dir, 0777, true)) {
  310.                 throw new \RuntimeException('Unable to create directory '.$dir);
  311.             }
  312.         }
  313.        
  314.         if (false === @file_put_contents($target, $asset->dump())) {
  315.             throw new \RuntimeException('Unable to write file '.$target);
  316.         }
  317.     }    
  318.    
  319.     private function _getAssetFullPath($asset)
  320.     {
  321.         return str_replace(DIRECTORY_SEPARATOR,'/',$asset->getRealPath());        
  322.     }
  323.    
  324.     private function _getAssetHash($asset)
  325.     {
  326.         return sha1($this->_getAssetFullPath($asset));
  327.     }
  328.    
  329.     private function _getBundleResourcesPath($bundle)
  330.     {                
  331.         return str_replace(DIRECTORY_SEPARATOR,'/',$bundle->getPath()) . '/Resources/public';
  332.     }
  333.        
  334. }