yii2: Assets not updating

I am experiencing a very odd issue regarding publishing assets (.js, .css). For some reason, Yii has stopped publishing updated .js files to the web/assets directory. I have to manually remove everything from web/assets for the updated version to show up. The reason this issue is odd is because the asset manager was working perfectly until last couple of days.

On my system I can see that my .js file was modified on a certain time, and the version of the same file in the assets directory has an earlier modification time; so I don’t think this is a browser caching issue. It’s an issue that updated assets are not being published.

I don’t have a clue where to start to finding out what is causing this. Does anyone have any idea on how to fix this?

About this issue

  • Original URL
  • State: closed
  • Created 9 years ago
  • Reactions: 6
  • Comments: 36 (27 by maintainers)

Most upvoted comments

I believe reason is the default hash function of AssetManager, It looks like below:

protected function hash($path)
    {
        if (is_callable($this->hashCallback)) {
            return call_user_func($this->hashCallback, $path);
        }
        $path = (is_file($path) ? dirname($path) : $path) . filemtime($path);
        return sprintf('%x', crc32($path . Yii::getVersion()));
    }

The value of $path is $sourcePath of an AssetBundle. When $sourcePath is a directory who has sub directory ,after some files are modified in the sub directory, the last modified time of the sub directory is changed ,but the directory 's last modified time is not change. The hash function only contain the directory’s last modified time, so publish js skipped.

I tried this for local development with good results:

     'hashCallback' => getenv('APP_ASSET_FORCE_PUBLISH') ? function ($path) {
                $files = \yii\helpers\FileHelper::findFiles(
                    $path, 
                    ['only' => ['*.js', '*.css', '*.less']]
                );
                foreach ($files as $file) {
                    $max = max(0, filemtime($file));
                }
                return hash('sha256', $path.$max);
            } : null,

I think this is definitely a problem. I’m having the same issue with another Yii2 project. Is nobody else having this issue?

@rubenjleon I did exactly the same, but problem remained. I learned there is another solution:

class Asset extends AssetBundle
{
    public $sourcePath = '...';
    public $js = [..];
    public $css = [...];
    public $depends = [...];

    public $publishOptions = [
        'forceCopy' => true,
         //you can also make it work only in debug mode: 'forceCopy' => YII_DEBUG
    ];
}

forceCopy forces files to be published in assets folder each time.

Same issue here.

My Assets bundle:

class AppAsset extends AssetBundle
{
    public $sourcePath = '@frontend/web/assets_src';
    public $jsOptions = array(
        'position' => \yii\web\View::POS_HEAD
    );
    public $css = [
        '//fonts.googleapis.com/css?family=Raleway:100,400,700',
        'css/layout.css',
        'css/ui.css',
        'css/typography.css',
        'css/icons.css',
        'css/main.css',
    ];
    public $js = [
        'js/html5.image.preview.js'
    ];
    public $depends = [
        'yii\bootstrap\BootstrapPluginAsset',
        'yii\web\JqueryAsset',
        'frontend\assets\DotDotDotAsset'
    ];

    /*public function __construct($config = [])
    {
        $this->publishOptions['forceCopy'] = (YII_ENV_DEV || YII_ENV_DEV);

        parent::__construct($config);
    }*/
}

As you can see I tried to add the publishOption to force copy assets, which works but slows down the website about 10 times.

It seems its not picking up the change only when you use sourcePath

Performance impact is negligible. For an asset bundle with 10 nested directories and 128 nested files, I’m seeing about an 0.2-second performance hit on average when calling getPublishedUrl() 1,000,000 times.

Are you testing this by calling getPublishedUrl() in a single loop? Do you call clearstatcache() after each cycle?

We are able to reliably reproduce this whenever a nested file has a modification date that is greater than the parent directory – especially easy to reproduce when the file is in a subdirectory of the asset bundle’s root path.

For Craft CMS, we’ve fixed this by overriding AssetManager::hash(), replacing the filemtime() call with a call to our craft\helpers\FileHelper::lastModifiedTime() method, which recursively loops through all sub-files/directories and returns the true latest modified time.

Performance impact is negligible. For an asset bundle with 10 nested directories and 128 nested files, I’m seeing about an 0.2-second performance hit on average when calling getPublishedUrl() 1,000,000 times.

@cebe @samdark @qiangxue Let me know if you’d like me to submit these changes in a PR.

Yes, the issue is there: http://stackoverflow.com/questions/3620684/directory-last-modified-date

The mtime (modification time) on the directory itself changes when a file or a subdirectory is added, removed or renamed.

Modifying the contents of a file within the directory does not change the directory itself, nor does updating the modified times of a file or a subdirectory. If you change the permissions on the directory, the ctime changes but the mtime does not.

same here. adding this to my config file made it work :

if (YII_ENV_DEV) {
    ...
    $config['components']['assetManager']['forceCopy'] = true;
}