8000 Update compiled views only if they actually changed · laravel/framework@663fa79 · GitHub
[go: up one dir, main page]

Skip to content

Commit 663fa79

Browse files
committed
Update compiled views only if they actually changed
When running in a read-only filesystem container with views pre-cached at build time, Laravel checks if the cached view's timestamp is older than the source's to decide when to recompile. This is fine as long as we do not make OCI image builds reproducible. If we do however, the timestamps match (because timestamps of all files are set to 01.01.1970), so Laravel recompiles the view, tries to write to a read-only filesystem and the container crashes. This patch computes SHA256 hashes over existing and newly compiled file contents, compares those and writes only if the file actually changes.
1 parent 22f61b3 commit 663fa79

File tree

2 files changed

+43
-1
lines changed

2 files changed

+43
-1
lines changed

src/Illuminate/View/Compilers/BladeCompiler.php

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,17 @@ public function compile($path = null)
193193
$compiledPath = $this->getCompiledPath($this->getPath())
194194
);
195195

196-
$this->files->put($compiledPath, $contents);
196+
if (! $this->files->exists($compiledPath)) {
197+
$this->files->put($compiledPath, $contents);
198+
199+
return;
200+
}
201+
202+
$contentHash = hash('sha256', $contents);
203+
$compiledHash = $this->files->hash($compiledPath, 'sha256');
204+
if ($compiledHash !== $contentHash) {
205+
$this->files->put($compiledPath, $contents);
206+
}
197207
}
198208
}
199209

tests/View/ViewBladeCompilerTest.php

Copy file name to clipboard
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ public function testCompileCompilesFileAndReturnsContents()
6666
$compiler = new BladeCompiler($files = $this->getFiles(), __DIR__);
6767
$files->shouldReceive('get')->once()->with('foo')->andReturn('Hello World');
6868
$files->shouldReceive('exists')->once()->with(__DIR__)->andReturn(true);
69+
$files->shouldReceive('exists')->once()->with(__DIR__.'/'.hash('xxh128', 'v2foo').'.php')->andReturn(false);
6970
$files->shouldReceive('put')->once()->with(__DIR__.'/'.hash('xxh128', 'v2foo').'.php', 'Hello World<?php /**PATH foo ENDPATH**/ ?>');
7071
$compiler->compile('foo');
7172
}
@@ -76,15 +77,42 @@ public function testCompileCompilesFileAndReturnsContentsCreatingDirectory()
7677
$files->shouldReceive('get')->once()->with('foo')->andReturn('Hello World');
7778
$files->shouldReceive('exists')->once()->with(__DIR__)->andReturn(false);
7879
$files->shouldReceive('makeDirectory')->once()->with(__DIR__, 0777, true, true);
80+
$files->shouldReceive('exists')->once()->with(__DIR__.'/'.hash('xxh128', 'v2foo').'.php')->andReturn(false);
7981
$files->shouldReceive('put')->once()->with(__DIR__.'/'.hash('xxh128', 'v2foo').'.php', 'Hello World<?php /**PATH foo ENDPATH**/ ?>');
8082
$compiler->compile('foo');
8183
}
8284

85+
public function testCompileUpdatesCacheIfChanged()
86+
{
87+
$compiledContent = 'Hello World<?php /**PATH foo ENDPATH**/ ?>';
88+
$compiler = new BladeCompiler($files = $this->getFiles(), __DIR__);
89+
$files->shouldReceive('get')->once()->with('foo')->andReturn('Hello World');
90+
$files->shouldReceive('exists')->once()->with(__DIR__)->andReturn(false);
91+
$files->shouldReceive('makeDirectory')->once()->with(__DIR__, 0777, true, true);
92+
$files->shouldReceive('exists')->once()->with($compiledPath)->andReturn(true);
93+
$files->shouldReceive('hash')->once()->with($compiledPath, 'sha256')->andReturn(hash('sha256', 'outdated content'));
94+
$files->shouldReceive('put')->once()->with($compiledPath, __DIR__.'/'.hash('xxh128', 'v2foo').'.php');
95+
$compiler->compile('foo');
96+
}
97+
98+
public function testCompileKeepsCacheIfUnchanged()
99+
{
100+
$compiledPath = __DIR__.'/'.hash('xxh128', 'v2foo').'.php';
101+
$compiler = new BladeCompiler($files = $this->getFiles(), __DIR__);
102+
$files->shouldReceive('get')->once()->with('foo')->andReturn('Hello World');
103+
$files->shouldReceive('exists')->once()->with(__DIR__)->andReturn(false);
104+
$files->shouldReceive('makeDirectory')->once()->with(__DIR__, 0777, true, true);
105+
$files->shouldReceive('exists')->once()->with($compiledPath)->andReturn(true);
106+
$files->shouldReceive('hash')->once()->with($compiledPath, 'sha256')->andReturn(hash('sha256', 'Hello World<?php /**PATH foo ENDPATH**/ ?>'));
107+
$compiler->compile('foo');
108+
}
109+
83110
public function testCompileCompilesAndGetThePath()
84111
{
85112
$compiler = new BladeCompiler($files = $this->getFiles(), __DIR__);
86113
$files->shouldReceive('get')->once()->with('foo')->andReturn('Hello World');
87114
$files->shouldReceive('exists')->once()->with(__DIR__)->andReturn(true);
115+
$files->shouldReceive('exists')->once()->with(__DIR__.'/'.hash('xxh128', 'v2foo').'.php')->andReturn(false);
88116
$files->shouldReceive('put')->once()->with(__DIR__.'/'.hash('xxh128', 'v2foo').'.php', 'Hello World<?php /**PATH foo ENDPATH**/ ?>');
89117
$compiler->compile('foo');
90118
$this->assertSame('foo', $compiler->getPath());
@@ -102,6 +130,7 @@ public function testCompileWithPathSetBefore()
102130
$compiler = new BladeCompiler($files = $this->getFiles(), __DIR__);
103131
$files->shouldReceive('get')->once()->with('foo')->andReturn('Hello World');
104132
$files->shouldReceive('exists')->once()->with(__DIR__)->andReturn(true);
133+
$files->shouldReceive('exists')->once()->with(__DIR__.'/'.hash('xxh128', 'v2foo').'.php')->andReturn(false);
105134
$files->shouldReceive('put')->once()->with(__DIR__.'/'.hash('xxh128', 'v2foo').'.php', 'Hello World<?php /**PATH foo ENDPATH**/ ?>');
106135
// set path before compilation
107136
$compiler->setPath('foo');
@@ -132,6 +161,7 @@ public function testIncludePathToTemplate($content, $compiled)
132161
$compiler = new BladeCompiler($files = $this->getFiles(), __DIR__);
133162
$files->shouldReceive('get')->once()->with('foo')->andReturn($content);
134163
$files->shouldReceive('exists')->once()->with(__DIR__)->andReturn(true);
164+
$files->shouldReceive('exists')->once()->with(__DIR__.'/'.hash('xxh128', 'v2foo').'.php')->andReturn(false);
135165
$files->shouldReceive('put')->once()->with(__DIR__.'/'.hash('xxh128', 'v2foo').'.php', $compiled);
136166

137167
$compiler->compile('foo');
@@ -187,6 +217,7 @@ public function testDontIncludeEmptyPath()
187217
$compiler = new BladeCompiler($files = $this->getFiles(), __DIR__);
188218
$files->shouldReceive('get')->once()->with('')->andReturn('Hello World');
189219
$files->shouldReceive('exists')->once()->with(__DIR__)->andReturn(true);
220+
$files->shouldReceive('exists')->once()->with(__DIR__.'/'.hash('xxh128', 'v2').'.php')->andReturn(false);
190221
$files->shouldReceive('put')->once()->with(__DIR__.'/'.hash('xxh128', 'v2').'.php', 'Hello World');
191222
$compiler->setPath('');
192223
$compiler->compile();
@@ -197,6 +228,7 @@ public function testDontIncludeNullPath()
197228
$compiler = new BladeCompiler($files = $this->getFiles(), __DIR__);
198229
$files->shouldReceive('get')->once()->with(null)->andReturn('Hello World');
199230
$files->shouldReceive('exists')->once()->with(__DIR__)->andReturn(true);
231+
$files->shouldReceive('exists')->once()->with(__DIR__.'/'.hash('xxh128', 'v2foo').'.php')->andReturn(false);
200232
$files->shouldReceive('put')->once()->with(__DIR__.'/'.hash('xxh128', 'v2').'.php', 'Hello World');
201233
$compiler->setPath(null);
202234
$compiler->compile();

0 commit comments

Comments
 (0)
0