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

Skip to content

Commit fe0de84

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 fe0de84

File tree

2 files changed

+43
-2
lines changed

2 files changed

+43
-2
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

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,17 +66,43 @@ 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
}
7273

7374
public function testCompileCompilesFileAndReturnsContentsCreatingDirectory()
7475
{
76+
$compiler = new BladeCompiler($files = $this->getFiles(), __DIR__);
77+
$files->shouldReceive('get')->once()->with('foo')->andReturn('Hello World');
78+
$files->shouldReceive('exists')->once()->with(__DIR__)->andReturn(true);
79+
$files->shouldReceive('exists')->once()->with(__DIR__.'/'.hash('xxh128', 'v2foo').'.php')->andReturn(false);
80+
$files->shouldReceive('put')->once()->with(__DIR__.'/'.hash('xxh128', 'v2foo').'.php', 'Hello World<?php /**PATH foo ENDPATH**/ ?>');
81+
$compiler->compile('foo');
82+
}
83+
84+
public function testCompileUpdatesCacheIfChanged()
85+
{
86+
$compiledContent = 'Hello World<?php /**PATH foo ENDPATH**/ ?>';
87+
$compiledPath = __DIR__.'/'.hash('xxh128', 'v2foo').'.php';
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(true);
91+
$files->shouldReceive('exists')->once()->with($compiledPath)->andReturn(true);
92+
$files->shouldReceive('hash')->once()->with($compiledPath, 'sha256')->andReturn(hash('sha256', 'outdated content'));
93+
$files->shouldReceive('put')->once()->with($compiledPath, $compiledContent);
94+
$compiler->compile('foo');
95+
}
96+
97+
public function testCompileKeepsCacheIfUnchanged()
98+
{
99+
$compiledPath = __DIR__.'/'.hash('xxh128', 'v2foo').'.php';
75100
$compiler = new BladeCompiler($files = $this->getFiles(), __DIR__);
76101
$files->shouldReceive('get')->once()->with('foo')->andReturn('Hello World');
77102
$files->shouldReceive('exists')->once()->with(__DIR__)->andReturn(false);
78103
$files->shouldReceive('makeDirectory')->once()->with(__DIR__, 0777, true, true);
79-
$files->shouldReceive('put')->once()->with(__DIR__.'/'.hash('xxh128', 'v2foo').'.php', 'Hello World<?php /**PATH foo ENDPATH**/ ?>');
104+
$files->shouldReceive('exists')->once()->with($compiledPath)->andReturn(true);
105+
$files->shouldReceive('hash')->once()->with($compiledPath, 'sha256')->andReturn(hash('sha256', 'Hello World<?php /**PATH foo ENDPATH**/ ?>'));
80106
$compiler->compile('foo');
81107
}
82108

@@ -85,6 +111,7 @@ public function testCompileCompilesAndGetThePath()
85111
$compiler = new BladeCompiler($files = $this->getFiles(), __DIR__);
86112
$files->shouldReceive('get')->once()->with('foo')->andReturn('Hello World');
87113
$files->shouldReceive('exists')->once()->with(__DIR__)->andReturn(true);
114+
$files->shouldReceive('exists')->once()->with(__DIR__.'/'.hash('xxh128', 'v2foo').'.php')->andReturn(false);
88115
$files->shouldReceive('put')->once()->with(__DIR__.'/'.hash('xxh128', 'v2foo').'.php', 'Hello World<?php /**PATH foo ENDPATH**/ ?>');
89116
$compiler->compile('foo');
90117
$this->assertSame('foo', $compiler->getPath());
@@ -102,6 +129,7 @@ public function testCompileWithPathSetBefore()
102129
$compiler = new BladeCompiler($files = $this->getFiles(), __DIR__);
103130
$files->shouldReceive('get')->once()->with('foo')->andReturn('Hello World');
104131
$files->shouldReceive('exists')->once()->with(__DIR__)->andReturn(true);
132+
$files->shouldReceive('e 57AE xists')->once()->with(__DIR__.'/'.hash('xxh128', 'v2foo').'.php')->andReturn(false);
105133
$files->shouldReceive('put')->once()->with(__DIR__.'/'.hash('xxh128', 'v2foo').'.php', 'Hello World<?php /**PATH foo ENDPATH**/ ?>');
106134
// set path before compilation
107135
$compiler->setPath('foo');
@@ -132,6 +160,7 @@ public function testIncludePathToTemplate($content, $compiled)
132160
$compiler = new BladeCompiler($files = $this->getFiles(), __DIR__);
133161
$files->shouldReceive('get')->once()->with('foo')->andReturn($content);
134162
$files->shouldReceive('exists')->once()->with(__DIR__)->andReturn(true);
163+
$files->shouldReceive('exists')->once()->with(__DIR__.'/'.hash('xxh128', 'v2foo').'.php')->andReturn(false);
135164
$files->shouldReceive('put')->once()->with(__DIR__.'/'.hash('xxh128', 'v2foo').'.php', $compiled);
136165

137166
$compiler->compile('foo');
@@ -187,6 +216,7 @@ public function testDontIncludeEmptyPath()
187216
$compiler = new BladeCompiler($files = $this->getFiles(), __DIR__);
188217
$files->shouldReceive('get')->once()->with('')->andReturn('Hello World');
189218
$files->shouldReceive('exists')->once()->with(__DIR__)->andReturn(true);
219+
$files->shouldReceive('exists')->once()->with(__DIR__.'/'.hash('xxh128', 'v2').'.php')->andReturn(false);
190220
$files->shouldReceive('put')->once()->with(__DIR__.'/'.hash('xxh128', 'v2').'.php', 'Hello World');
191221
$compiler->setPath('');
192222
$compiler->compile();
@@ -197,6 +227,7 @@ public function testDontIncludeNullPath()
197227
$compiler = new BladeCompiler($files = $this->getFiles(), __DIR__);
198228
$files->shouldReceive('get')->once()->with(null)->andReturn('Hello World');
199229
$files->shouldReceive('exists')->once()->with(__DIR__)->andReturn(true);
230+
$files->shouldReceive('exists')->once()->with(__DIR__.'/'.hash('xxh128', 'v2').'.php')->andReturn(false);
200231
$files->shouldReceive('put')->once()->with(__DIR__.'/'.hash('xxh128', 'v2').'.php', 'Hello World');
201232
$compiler->setPath(null);
202233
$compiler->compile();

0 commit comments

Comments
 (0)
0