reliable cache busting solution #237

Open
lsmith77 opened this Issue May 23, 2012 · 2 comments

Comments

Projects
None yet
3 participants
@lsmith77

we found that using the assets_version as supported by AsseticBundle isn't really ideal for cache busting. For one many proxies tend to have issues with GET parameters for js/css assets. Furthermore its hard to determine when to increment the version.

as a quick fix for this we created the following command, but it basically requires you to update the actual templates with a hash. the good news is that you can run the command as often as you want .. it will be deterministic, but is it the best solution?


<?php

namespace Liip\AssetsBundle\Command;

use Assetic\Asset\AssetInterface;
use Assetic\Factory\LazyAssetManager;
use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Finder\Finder;

/**
 * updates asset collection file names according to its content and some hash of it
 *
 * @author christian stocker <me@chregu.tv>
 */
class HashCommand extends ContainerAwareCommand
{
    private $basePath;
    private $verbose;
    private $am;

    protected function configure()
    {
        $this
            ->setName('liip:update_asset_names')
            ->setDescription('Dumps all asset hashes to the twig templates in output attributes')
        ;
    }

    protected function initialize(InputInterface $input, OutputInterface $output)
    {
        parent::initialize($input, $output);

        $this->verbose = $input->getOption('verbose');
        $this->am = $this->getContainer()->get('assetic.asset_manager');
    }

    protected function execute(InputInterface $input, OutputInterface $output)
    {
        $output->writeln(sprintf('Hashing all <comment>%s</comment> assets.', $input->getOption('env')));
        $output->writeln(sprintf('Debug mode is <comment>%s</comment>.', $input->getOption('no-debug') ? 'off' : 'on'));

        $paths = array();
        $changedFiles = array();

        foreach ($this->am->getNames() as $name) {
            $asset = $this->am->get($name);
            $md5 = '';
            foreach ($asset->all() as $a) {
                $a->load();
                $md5 .= md5($a->getContent());
            }

            $md5 = substr(md5($md5),0,10);
            $outputPath = str_replace('_controller/', '', $asset->getTargetPath());

            $newOutputPath = preg_replace("/\/[0-9a-z]{1,32}\.([a-z]{2,3})/","/".$md5.".$1",$outputPath);
            if (preg_match("/\/[0-9a-z]{7}\./",$outputPath)) {
                $outputPath = preg_replace("/\/[0-9a-z]{7}\./","/*.", $outputPath);
            }

            if ($outputPath != $newOutputPath) {
                $finder = new Finder();

                $finder->in(getcwd());
                $finder->files()->name('*.twig');
                foreach ($finder as $f) {
                    $contentold = file_get_contents($f->getRealPath());
                    $content = str_replace(" output='$outputPath'"," output='$newOutputPath'", $contentold);
                    if ($contentold != $content) {
                        $output->writeln("content changed in " . $f->getRealPath() . " from $outputPath to $newOutputPath");
                        $changedFiles[] = $f->getRealPath();
                        file_put_contents($f->getRealPath(), $content);
                    }
                }
            }
        }

        if ($changedFiles) {
            $output->writeln("Some files changed, don't forget to add them to git:");
            foreach ($changedFiles as $file) {
                $output->writeln("git add $file");
            }
            $output->writeln('git commit -m "update hash file names of css and js files"');
        }

        return $changedFiles ? 0 : 1;
    }
}
@rickard2

This comment has been minimized.

Show comment
Hide comment
@rickard2

rickard2 Feb 8, 2013

This is the best approach that I've found when developing similar solutions for our own applications. I would love to see this as an option directly in assetic so that dumping the assets always would calculate the name based on content.

rickard2 commented Feb 8, 2013

This is the best approach that I've found when developing similar solutions for our own applications. I would love to see this as an option directly in assetic so that dumping the assets always would calculate the name based on content.

@stof

This comment has been minimized.

Show comment
Hide comment
Collaborator

stof commented Feb 11, 2013

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment